Windows系统的多显示器模式的编程.doc_第1页
Windows系统的多显示器模式的编程.doc_第2页
Windows系统的多显示器模式的编程.doc_第3页
Windows系统的多显示器模式的编程.doc_第4页
Windows系统的多显示器模式的编程.doc_第5页
已阅读5页,还剩3页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Windows系统的多显示器模式的编程这几天研究了一下Windows系统的多显示器模式的编程,实现了Windows下支持10显示器模式的通用com组件,这里做一个整理和回顾,希望能对再这方面开发的兄弟们有些启发和帮助: (一)Windows系统下的多显示器模式的原理 Microsoft新的操作系统(Windows98Windows2000WindowsXP)内置了对多监视器的支持,即用户可以在一台计算机上安装多个显示卡并接上多个显示器,然后把这些显示器的显示区域组织成一个大的虚拟的Windows桌面。每一个显示区域的底部都有系统任务栏,我们可以在任何一个显示区域内增加桌面快捷方式,这样就可以在第一个显示区域上用VisualC编程,同时在第二个显示区域上打开InternetExplorer上网再也不用进行麻烦的切换了。 多显示器模式的原理实际上很简单,主要还是要靠操作系统的支持,比如WinXP就支持10个显示器,本文所使用的调试和开发环境都是以WinXP为主,其余的原理都相同慢慢调试就行了. Windows提供的多显示器模式主要有以下三个功能: 1.更大的Windows桌面:在多显示器模式下,可以把多个显示器的显示区域结合在一起来显示Windows桌面,不管这些显示器的尺寸、物理位置、分辨率和刷新频率是否相同。当我们运行一个应用程序时,程序的主窗口可以位于任何一个显示器的显示区域内,也可以跨多个显示区域。我们也可以把一个程序的窗口从一个显示区域移到另一个显示区域中。 2.屏幕复制或远程显示:我们可以让两个显示器显示相同的内容。在进行培训或者向众人进行演示时,这个特点是很有用的。利用这个特性,技术支持人员还可以对应用程序进行远程监视和调试。 3.多重独立显示:在以上的两种模式下,所有的显示区域都是Windows虚拟桌面的一部分,但是在多重独立显示模式下,应用程序访问的显示器并不属于Windows虚拟桌面。假设系统的第二个显示器是一个高分辨率的大尺寸显示器,我们可以把它用做CAD应用程序的专用显示。通过在CAD应用程序中调用新的WindowsAPI,我们可以借助GDI在上面画图。独立显示器的显示区域没有桌面上的任何对象(任务栏和快捷方式),它与Windows桌面是独立的。这可以避免Windows桌面对应用程序输出的任何干扰,我们也不用担心会在无意中把其它的窗口拽到独立显示的显示区域中,这种方式就好像为应用程序提供了一个专用的显示器。 (二)理解虚拟桌面(VirtualDesktop)及其坐标 既然是要对多显示器模式进行编程和开发,那么我们就要首先理解Windows的虚拟桌面(VirtualDesktop)及其坐标了.这是我们编程开发的基础,理解了一切就很顺利了,几乎没有什么难度. 在单显示器系统中,实际Windows桌面的形状和大小与显示器是相同的。在多显示器模式下,每一个显示器实际上是一个大虚拟桌面的一个“子视窗”。 我们可以通过控制面板中的显示器属性对每一个显示器的显示区域的大小(分辨率)和相对位置进行调整,所有这些显示区域互相连接但并不重叠。图一中的显示器1是主显示器,主显示器的作用是确定虚拟桌面的坐标。不管主显示器的位置如何,它的显示区域的左上角的坐标定为虚拟坐标的零点(0,0),右下角的坐标是(X1,Y1)(假设主显示器的分辨率为XY),其余显示区域的坐标由它和主显示器的相对位置决定。通常虚拟桌面中显示区域的相对位置和实际显示器的物理相对位置是相同的。因为所有显示区域必须相连,因此可以用一个包含所有显示区域的最小矩形来表示虚拟桌面的大小。图一中的矩形边界代表了虚拟桌面的范围。 Page因为虚拟桌面中的坐标系统必须是连续的,因此第二个显示区域的坐标是主显示器的显示区域的继续。假设两个显示器都使用1024768的分辨率,并且第二个显示器位于第一个显示器(主显示器)的正右方,则第二个显示区域的坐标是从(1024,0)到(2047,767)。 但是并不是所有的显示区域都具有相同的分辨率,而且这些显示区域也不一定是底边对齐的。就像图一中显示的那样,你真正能看到的有效显示区域是红色兰色紫色的不规则区域,而黄色区域虽然也属于虚拟桌面的一部分,但它不属于任何一个显示区域,这部分也叫做无效区域。如图一中所示,假设显示器1的分辨率是1024768,显示器2的分辨率为800600,显示器3的分辨率为640480。零点的位置如图中所示,显示器1的坐标为(0,0)到(1023,767),显示器2的坐标为(800,168)到(1,767),显示器3的坐标是(1024,0)到(1663,479)。而(800,0)到(1,167)以及(1024,480)到(1663,767)这两块无效区域是不能显示任何信息的,系统不会允许用户把鼠标移动到这两个区域。需要注意的是无效区域是包括在虚拟桌面中的,因此图一中的虚拟桌面的大小是从(800,0)到(1663,767)。 我在编程开发的过程中就使用了2个显示器,一个是自己的笔记本,分辨率为1024768作为主显示器,另外一个由于比较懒,直接找了一个小巧的NEC12寸屏幕的小黑白显示器,不是为了别的搬着方便啊,这个NEC黑白支持分辨率800600,强吧. 如下图我是直接设置了扩展桌面,两个显示器就都可以使用了 在这里要注意主显示器和副显示器的区别,其实主显示器和副显示器你是可以进行任意调整的. (三)系统支持编程开发的API Microsoft为支持多显示器模式提供了一些新的API调用,下面具体介绍它们的功能: 1.HMONITORMonitorFromPoint(POINTpt,DWORDdwFlags) MonitorFromPoint返回包含特定点(pt)的一个显示器句柄。如果pt不属于任何一个显示器,返回的显示器句柄由dwFlags标志决定:MONITOR_DEFAULTTONULL时返回NULL,MONITOR_DEFAULTTOPRIMARY时返回代表主显示器的HMONITOR句柄,MONITOR_DEFAULTTONEAREST时返回最靠近pt点的显示器的HMONITOR句柄。2.HMONITORMonitorFromRect(LPCRECTlprc,DWORDdwFlags) MonitorFromRect返回包含lprc代表的矩形的显示器句柄;如果包含此矩形的显示区域不止一个,则返回包含矩形最大部分的显示器句柄;如果矩形不属于任何一个显示区域,返回的句柄由dwFlags决定,规则与MonitorFromPoint相同。 3.HMONITORMonitorFromWindow(HWNDhwnd,DWORDdwFlags)与MonitorFromRect类似,但输入是一个代表窗口的句柄hwnd而不是指向矩形的指针。 4.BOOLGetMonitorInfo(HMONITORhMonitor,LPMONITORINFOlpmi) GetMonitorInfo返回由hMonitor代表的显示器的有关信息,这些信息存储在指向MONITORINFO结构的指针lpmi中。这些信息包括用RECT结构表示的显示器的显示区域的大小(如果这个显示器不是主显示器,RECT的坐标可能为负数),以及用RECT结构表示的显示器的工作区域的大小,工作区域是显示区域中除去系统任务栏和应用程序快捷方式栏所剩下的区域,还能够判断此显示器是否为主显示器,并返回一个标志。 Page5.BOOLEnumDisplayMonitors(HDChdc,LPCRECTlprcClip,MONITORENUMPROClpfnEnum,LPARAMdwData) hdc是一个代表显示设备环境的句柄,lprcClip是指向一个矩形区域的指针。把这个矩形区域和设备环境中的可见区域取交集,得到的区域可能分布在多个显示器的显示区域中,EnumDisplayMonitors对每一个包含交集的显示区域调用一次MonitorEnumProc类型的函数。DwData为传递给MonitorEnumProc函数的数据。 6.BOOLCALLBACKMonitorEnumProc(HMONITORhmonitor,HDChdcMonitor,LPRClprcMonitor,DWORDdwData) MonitorEnumProc是一个被EnumDisplayMonitors函数调用的回调函数,它的内容可以由用户自定义。利用这两个函数,用户在进行跨多个显示器的显示时就可以利用每一个显示器的不同的显示特性。 当然,并不是所有画图程序都必须调用这两个函数,这时你假设所有的显示器都使用同样颜色的分辨率。 7.EnumDisplayDevices(LPVOIDlpReserved,intiDeviceNum,DISPLAY_DEVICEpDisplayDevice,DWORDdwFlags) EnumDisplayDevices列出系统中某个显示设备(以iDeviceNum为序号)的信息。与GetMonitorInfo相比,GetMonitorInfo对应的显示器必须是Windows虚拟桌面的一部分,而EnumDisplayDevices可以列出包括处于独立显示模式下的系统所安装的所有显示器的信息。它返回的信息储存在DISPLAY_DEVICE结构中,包括显示设备名称、对显示设备的描述和显示设备的状态。 此外,一些原有的API调用如SystemParametersInfo和GetSystemMetrics也加入了对多显示器模式的支持。比如调用GetSystemMetrics时,如果用SM_XVIRTUALSCREEN、SM_YVIRTUALSCREEN、SM_CXVIRTUALSCREEN和SM_CYVIRTUALSCREEN,得到的是虚拟桌面左上角的坐标和整个的长度和宽度。 我们在编程时特别要注意坐标的变化:首先单显示器下负坐标或大于SM_CXSCREEN和SM_CYSCREEN部分的窗口将被隐藏,而在多显示器模式下这些都是合法的。其次在确定应用程序窗口和对话框的位置时,要选择正确的显示器和正确的全局坐标(虚拟桌面坐标)。最后,在恢复原来存储的窗口之前,要检查一下这些窗口坐标的有效性。 这些都可以在微软的MSDN上去查出来,需要仔细的看一看,每个API都亲自试一试. 大家可以参考MSND的一篇文章HowtoExploitMultipleMonitorSupportinMemphisandWindowsNT5.0,说的很详细. (四)实现多屏幕编程的组件设计 这个组件参考了网上的许多资料,这里先向那些无私的同行表示感谢,我做的工作只是将他们的成果进行了系统化的整理. 组件的设计流程如下: (1).初始化程序 Syntax:MScreenInfo(); Description:部件构造函数,初始化部件,获取系统屏幕信息,设置部件属性。 (2).获取指定屏幕的宽度 Syntax:ShortGetScreenWidth(ShortScreenNo); PageInput:ScreenNo-指定屏幕的序号,0-m_monitorNum-1; Return:ScreenWidthinPixel; Decription:获取ScreenNo指定屏幕的宽度。 (3).获取指定屏幕的高度 Syntax:ShortGetScreenHeight(ShortScreenNo); Input:ScreenNo-指定屏幕的序号,0-m_monitorNum-1; Return:ScreenHeightinPixel; Decription:获取ScreenNo指定屏幕的高度。 程序流程图:与图2相同,只是最后一步返回dm.dmPelsHeight. (4).获取指定屏幕的坐标原点-left Syntax:ShortGetScreenLeft(ShortScreenNo); Input:ScreenNo-指定屏幕的序号,0-m_monitorNum-1; Return:ScreenLeftinPixel; Decription:获取ScreenNo指定屏幕的坐标原点-left。 程序流程图:与图2相同,只是最后一步返回dm.dmPosition.x. (5).获取指定屏幕的坐标原点-top Syntax:ShortGetScreenLeft(ShortScreenNo); Input:ScreenNo-指定屏幕的序号,0-m_monitorNum-1; Return:ScreenTopinPixel; Decription:获取ScreenNo指定屏幕的坐标原点-top。 程序流程图:与图2相同,只是最后一步返回dm.dmPosition.y. (6).获取主屏幕-PrimaryScreen Syntax:ShortGetPrimaryScreen(); Input:Null; Return:PrimaryScreenNo,0-m_monitorNum-1 Description:获取主屏幕的序号。 程序流程:依次判断那一个屏幕的原点是(0,0). (五)组件开发的实现和主要代码 1开发环境 操作系统:WindowsXP编程环境:VC6.0 2组件接口如下 3主要代码 /获得显示器的数量 CMScreenInfoCtrl:CMScreenInfoCtrl() InitializeIIDs(&IID_DMScreenInfo,&IID_DMScreenInfoEvents);/找出显示器的总数量 inti; BOOLflag; DISPLAY_DEVICEdd; i=0; flag=true; ZeroMemory(&dd,sizeof(dd); dd.cb=sizeof(dd); do flag=EnumDisplayDevices(NULL,i,&dd,0); if(flag)i+=1; while(flag); m_monitorNum=i;/总数量 /获得显示区宽度 shortCMScreenInfoCtrl:GetScreenWidth(shortScreenNo) if(ScreenNo=m_monitorNum)return0; PageBOOLflag; DISPLAY_DEVICEdd; ZeroMemory(&dd,sizeof(dd); dd.cb=sizeof(dd); flag=EnumDisplayDevices(NULL,ScreenNo,&dd,0); if(!flag)return0; DEVMODEdm; ZeroMemory(&dm,sizeof(dm); dm.dmSize=sizeof(dm); flag=EnumDisplaySettings(char*)dd.DeviceName,ENUM_CURRENT_SETTINGS,&dm); if(!flag)return0; return(short)dm.dmPelsWidth; /设置显示区宽度 voidCMScreenInfoCtrl:SetScreenWidth(shortScreenNo,shortnNewValue) SetModifiedFlag(); /获得显示区宽度 shortCMScreenInfoCtrl:GetScreenHeight(shortScreenNo) if(ScreenNo=m_monitorNum)return0; BOOLflag; DISPLAY_DEVICEdd; ZeroMemory(&dd,sizeof(dd); dd.cb=sizeof(dd); flag=EnumDisplayDevices(NULL,ScreenNo,&dd,0); if(!flag)return0; DEVMODEdm; ZeroMemory(&dm,sizeof(dm); dm.dmSize=sizeof(dm); flag=EnumDisplaySettings(char*)dd.DeviceName,ENUM_CURRENT_SETTINGS,&dm); if(!flag)return0; return(short)dm.dmPelsHeight; /设置显示区高度 voidCMScreenInfoCtrl:SetScreenHeight(shortScreenNo,shortnNewValue) SetModifiedFlag(); /获得显示区Y坐标 shortCMScreenInfoCtrl:GetScreenTop(shortScreenNo) if(ScreenNo=m_monitorNum)return-1; BOOLflag; DISPLAY_DEVICEdd; ZeroMemory(&dd,sizeof(dd); dd.cb=sizeof(dd); flag=EnumDisplayDevices(NULL,ScreenNo,&dd,0); if(!flag)return-1; DEVMODEdm; ZeroMemory(&dm,sizeof(dm); dm.dmSize=sizeof(dm); flag=EnumDisplaySettings(char*)dd.DeviceName,ENUM_CURRENT_SETTINGS,&dm); if(!flag)return-1; return(short)dm.dmPosition.y; /设置显示区Y坐标 voidCMScreenInfoCtrl:SetScreenTop(shortScreenNo,shortnNewValue) SetModifiedFlag(); /获得显示区X坐标 shortCMScreenInfoCtrl:GetScreenLeft(shortScreenNo) Page if(ScreenNo=m_monitorNum)return-1; BOOLflag; DISPLAY_DEVICEdd; ZeroMemory(&dd,sizeof(dd); dd.cb=sizeof(dd); flag=EnumDisplayDevices(NULL,ScreenNo,&dd,0); if(!flag)return-1; DEVMODEdm; ZeroMemory(&dm,sizeof(dm); dm.dmSize=sizeof(dm); flag=EnumDisplaySettings(char*)dd.DeviceName,ENUM_CURRENT_SETTINGS,&dm); if(!flag)return-1; return(short)dm.dmPosition.x; /设置显示区X坐标 voidCMScreenInfoCtrl:SetScreenLeft(shortScreenNo,shortnNewValue) SetModifiedFlag(); /获得主显示区 shortCMScreenInfoCtrl:GetPrimaryScreen() /TODO:Addyourpropertyhandlerhere if(m_monitorNum=1)return0; /iftheScreenTop=0andLeft=0,then,itsthePrimaryScreen shorti; for(i=0;im_monitorNum;i+) if(GetScreenTop(i)=0&GetScreenLeft(i)=0)returni; return0; /设置主显示区 voidCMScreenInfoCtrl:SetPrimaryScreen(shortnNewValue) SetModifiedFlag(); 关键的代码基本就是这些了. (3)组件发布 直接编译成为ocx组件,取名为MutlScreen.ocx 使用regsvr32.exe注册一下就可以使用了. (六)分屏输出组件的应用 我们使用最简单的VB来编写一个小程序实现 1建立一个VB的工程,引用组件,建立两个form,分别为frmCtl,和frmOutScreen,我们将frmOutScreen输出到第二个屏幕上; 2组件使用的代码如下:

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论