当前位置: 首页 > 文档资料 > DirectX 中文教程 >

多显示器系统

优质
小牛编辑
120浏览
2023-12-01

多显示器系统

返回上级

Windows 98和Windows NT 5.0可以支持在一个单独的系统中存在多个显示设备和显示器。多显示器架构(通常简称为“MultiMon”)使操作系统使用两个或多个显示设备和显示器来建立一个逻辑桌面成为可能。举例来说,在一个有两台显示器的系统中,用户可以在其中的任何一个显示器上显示他的应用程序,或者将窗口从一个窗口中拖到另一个窗口中。

这一章的内容包含了如何在一个多显示器系统中使用DirectDraw的信息。讨论了一下的主题。

  1. 在多显示器系统中列举显示设备
  2. 焦点窗口和设备窗口
  3. 设置焦点窗口和设备窗口
  4. 缺省设备窗口
  5. 多显示器系统中的显示设备与加速特性

DirectX SDK的\Samples\Misc目录中包含了一些文件,提供了用于多显示器系统中的辅助性的函数。Multimon.h头文件使在Windows 98多显示器系统下编译的源代码能够在使用Windows 95的机器中顺利的编译和执行。除此之外,Ddmm.cpp文件Ddmm.h文件提供了辅助性的函数,允许你从一个窗口句柄或设备字符串中创建或获得DirectDraw对象。

在多显示器系统中列举显示设备

返回目录

使用DirectDrawEnumerateEx函数可以列举出在多显示器系统中的各显示设备,指定一个标志来决定哪种类型的DirectDraw设备可以被列举出来。该函数每列举出一个设备,就调用一次应用程序定义的DDEnumCallbackEx型回调函数。

  • 因为显示设备包括了显示卡和显示器,而且在通常情况下,一个显卡对应一个显示器,为了便于理解,以后我们谈到的显示设备,读者可以简单的将其直接视为显示器。

DirectDrawEnumerateEx函数被Windows 98和Windows NT 5.0(或更高版本)所支持,并且只能通过从动态连接库中获得函数地址来调用。在运行期,它是这样实现的:调用GetProcAddress Win32函数,从Ddraw.dll动态连接库中载入函数的地址。下面的例程演示了这个方法。

HINSTANCE h = LoadLibrary("ddraw.dll");

// 如果ddraw.dll不存在搜索的路径中,

// 那么,可能还没有安装DirectX,返回失败。

if (!h)

return FALSE;

// 注意:你必须知道要获得的函数的版本,

// 在这个例子中,我们使用ANSI版本。

LPDIRECTDRAWENUMERATEEX lpDDEnumEx;

lpDDEnumEx = (LPDIRECTDRAWENUMERATEEX) GetProcAddress(h,"DirectDrawEnumerateExA");

// 如果函数存在,调用它,列举出所有连接到桌面的显示设备,

// 以及所有非显示的DirectDraw设备。

if (lpDDEnumEx)

lpDDEnumEx(Callback, NULL,

DDENUM_ATTACHEDSECONDARYDEVICES |

DDENUM_NONDISPLAYDEVICES

);

else

{

/*

* 我们一定是运行在一个较老版本的DirectDraw中。

* 因此,该操作系统必定不支持多显示器。

* 于是,返回到DirectDrawEnumerate函数以列举出单显示器的标准显示设备。

*/

DirectDrawEnumerate(OldCallback,NULL);

/* 注意:这里有一个小的技巧,

* 让OldCallback 回调函数包装进DDEnumCallbackEx回调函数。

* 最后的回调函数是下面的这个样式:

* BOOL FAR PASCAL OldCallback(GUID FAR *lpGUID,

* LPSTR pName,

* LPSTR pDesc,

* LPVOID pContext)

* {

* return Callback(lpGUID,pName,pDesc,pContext,NULL);

* }

*/

}

// 最后,必须调用FreeLibrary函数释放动态连接库。

FreeLibrary(h);

上面的例程代码可以在运行期或装载期为连接到Ddraw.dll的应用程序所用。

应该注意的是,你必须获得DirectDrawEnumerateEx 函数的ANSI或Unicode版本中的哪一个,依赖于你的应用程序的字符串类型。当声明相应的回调函数时,为字符串参数使用LPTSTR数据类型。如果你声明了_UNICODE符号,LPTSTR数据类型在编译时使用Unicode字符串,否则使用ANSI字符串。通过使用LPTSTR数据类型,该函数能够正常工作,而不论你的应用程序使用的是什么样的字符串。

焦点窗口与设备窗口

返回目录

在单独显示器系统中,当你为一个应用程序设置了全屏独占的控制级时(通过调用IDirectDraw2::SetCooperativeLevel函数),你必须指定你的应用程序窗口句柄。DirectDraw使用应用程序的窗口句柄来勾住系统的消息,用来获得应用程序的状态信息、传递键盘输入的消息、和调整窗口大小的消息等。DirectDraw不需要为普通控制级(窗口模式)的应用程序设置一个窗口句柄。

因为多显示器系统必不可少的会用到至少一个显示设备,因此,要用DirectDraw访问到每一个设备, 你必须为每一个显示设备创建一个DirectDraw对象。然而,你所创建的每一个DirectDraw对象需要知道哪一个应用程序的窗口是它可以控制的,以及哪一个是可以接收到击键消息的。换句话说,应用程序需要有一个方法来告诉DirectDraw哪一个窗口将被用于哪些操作。这个特性是通过焦点窗口(Focus window)和设备窗口(Device window)的概念提供的。IDirectDraw2::SetCooperativeLevel函数支持三个标志符使这个特性连接到一个DirectDraw对象:DDSCL_SETFOCUSWINDOW、DDSCL_SETDEVICEWINDOW、和DDSCL_CREATEDEVICEWINDOW。

一个“设备窗口”只是一个简单的可见窗口,DirectDraw可以调整其大小以占据显示器的整个显示区域。你可以手工创建与安装与显示设备一样多的设备窗口,或者你可以让DirectDraw来为你处理这些细节(当你让DirectDraw来管理这些设备窗口的创建时,会存在一些协定,这些协定将在“缺省设备窗口”一节中讨论)。

一个焦点窗口指的是DirectDraw对象可以对其发送击键消息的窗口。一个DirectDraw应用程序只能拥有一个焦点窗口。

要得到更多的资料,请参阅“设置焦点和设备窗口”和“缺省设备窗口”。

设置焦点窗口和设备窗口

返回目录

调用IDirectDraw2::SetCooperativeLevel函数,并且指定DDSCL_SETDEVICEWINDOW或DDSCL_SETFOCUSWINDOW标志,你可以设置一个DirectDraw对象的焦点窗口和设备窗口。(SetCooperativeLevel函数同样接受DDSCL_CREATEDEVICEWINDOW标志,要得到更多的资料,请参阅“缺省设备窗口”。)

注意:只有当你想要获得对多显示器的全屏独占的访问时,才能设置焦点和设备窗口。如果你只想对主显示设备和显示器获得独占访问,你可以调用SetCooperativeLevel函数,在dwFlags参数中仅仅指定DDSCL_FULLSCREEN和DDSCL_EXCLUSIVE标志。

设置焦点和设备窗口是一个两步的过程:在为一个显示设备创建了DirectDraw对象之后,你必须首先为它设置焦点窗口。焦点窗口对你的应用程序中所有DirectDraw对象来说都是同一个,并且通过调用SetCooperativeLevel函数来设置。在调用中,第一个参数是获取击键消息的窗口的句柄,第二个参数是DDSCL_SETFOCUSWINDOW标志。

在设置了DirectDraw对象的焦点窗口后,你应该设置设备窗口和控制级(在这种情况下,为全屏独占模式)。这一步是通过另一次调用SetCooperativeLevel函数完成的。在这一次的调用中,第一个参数是DirectDraw将调整其大小为全屏的窗口的句柄,第二个参数是DDSCL_SETDEVICEWINDOW、DDSCL_FULLSCREEN、和DDSCL_EXCLUSIVE标志的结合。

尽管你必须指定一个焦点窗口,你仍然可以让DirectDraw来操纵你的设备窗口的创建和管理工作。要得到更多的资料,请参阅“缺省设备窗口”。

缺省设备窗口

返回目录

你可以选择让DirectDraw来为你完成创建、管理、和销毁设备窗口的工作。简单的说,DirectDraw管理的窗口被称为是“缺省设备窗口”。尽管使用缺省设备窗口可以把你从创建、管理、销毁设备窗口的繁重任务中解放出来,但是你的应用程序将不能够获得操作系统发送到那些缺省设备窗口的鼠标消息。因此,只有在你的应用程序不需要从那些设备窗口接收鼠标消息的情况下,你才应该选择使用缺省设备窗口。

启用缺省设备窗口的过程是很简单的。在首先调用IDirectDraw2::SetCooperativeLevel函数设置了一个DirectDraw对象的焦点窗口之后,再次调用该函数,将dwFlags参数指定为DDSCL_CREATEDEVICEWINDOW、DDSCL_FULLSCREEN和DDSCL_EXCLUSIVE标志符的结合。

注意:在一个单步调用SetCooperativeLevel函数的过程中,设置一个DirectDraw对象的焦点窗口、告诉DirectDraw创建一个缺省设备窗口、并且设置DirectDraw的控制级也是可能的。下面的例程演示了这是怎样完成的:

/*

* 该代码片段只在使用缺省设备窗口时才可用.

*/

HRESULT hr;

hr = g_lpDD->SetCooperativeLevel(

// 该窗口句柄仅仅是焦点窗口的

hwndFocus,

DDSCL_SETFOCUSWINDOW | DDSCL_FULLSCREEN |

DDSCL_EXCLUSIVE | DDSCL_CREATEDEVICEWINDOW);

多显示器系统中的显示设备与加速特性

返回目录

一个DirectDraw应用程序应该列举出所有显示设备,并且从中选择一个显示设备(或让用户来选择),然后通过使用它的硬件全局唯一标志符(GUID)为该显示设备创建一个DirectDraw对象。这种技术可以使无论是在多显示器系统还是单显示器系统中,以及在任何一种控制级上都能获得最好的显示效果。

当前所激活的显示设备也被称为“缺省设备(Default device)”,或“空设备(Null device)。后一个名称的得来是因为当前所激活的显示设备是将NULL作为它的GUID而列举出来的。许多现有的应用程序为缺省设备创建一个DirectDraw对象,并且假定该设备是硬件加速的。然而,在多显示器系统上,缺省设备并不一定是硬件加速的,这取决于当时对DirectDraw所设置的控制级。

在全屏独占模式中,缺省设备是硬件加速的,但是已安装的其它显示设备则不是。这意味着,具有全屏独占模式的应用程序在多显示器系统中可以和任何其它系统一样运行得非常快速,但是不能利用系统内置的跨显示器的图形操作。需要使用到多显示设备(显示器)的全屏独占模式的应用程序,可以为每一个他想要使用的显示设备(显示器)创建一个DirectDraw对象。应注意的是:为一个特定的设备创建一个DirectDraw对象,你必须提供该设备的GUID(在调用DirectDrawEnumerate函数时可以被列举出来)。

当你把控制级设置为普通(窗口模式)时,缺省设备则不具有硬件加速性;此时的缺省设备成为了一个有效的、将两个物理显示设备的资源联合起来的逻辑仿真显示设备,因此,当设置了控制级为普通时,缺省设备是根本没有被硬件加速的。从另一个方面来说,当设置了控制级为普通时,缺省设备自动具有了跨越各显示器(显示设备)的图形操作的能力。典型的实例是,当第二显示器的逻辑地址位于主显示器的左边时,对主显示器进行带负坐标的Blit操作是有效的。

如果了你给应用程序设置控制级为普通模式,却仍需要利用硬件的加速特性,你必须只创建一个单独的DirectDraw对象,使用一个特定显示设备的GUID。应注意的是:如果你不使用缺省设备,你不会获得自动跨越设备的能力。这就是说,对主页面的Blit操作超出了显示器边界的部分将会被裁减掉(如果你使用了裁减器),或操作失败,返回DDERR_INVALIDRECT的错误。

作为所有系统上的惯例,在创建了DirectDraw对象后,获得其能力或查询其它接口之前,你应该立即设置其控制级。除此之外,还应该避免在多显示器系统中多次设置控制级。如果你的应用程序需要从全屏模式切换到窗口模式运行,最好的做法是新建一个DirectDraw对象。