MSDN Home > MSJ > June 1997 |
How to Exploit Multiple Monitor Support in Memphis and Windows NT 5.0 |
| Have you noticed how precious screen real estate has become? The monitor never seems big enough and the resolution never seems high enough. I want to see Outlook™, Microsoft® Internet Explorer, Microsoft Developer Studio, and the app that I'm debugging all at the same time—I just don't have enough room!
|
![]() |
|
![]() |
|
HMONITOR MonitorFromPoint(POINT pt, DWORD dwFlags); |
| If no monitor contains pt, the return value depends upon the dwFlags field, which can be MONITOR_DEFAULTTONULL to return NULL, MONITOR_DEFAULTTOPRIMARY to return the HMONITOR of the primary monitor, or MONITOR_DEFAULTTONEAREST to return the HMONITOR nearest to the point pt. |
HMONITOR WINUSERAPI MonitorFromRect(LPCRECT lprc, |
| If no monitor intersects lprc, the return value depends upon the dwFlags field. The flags from MonitorFromPoint are used. If the rect intersects more than one monitor, this returns the monitor containing most of the rectangle. |
HMONITOR WINUSERAPI MonitorFromWindow(HWND hwnd, |
| If a window doesn't belong to a monitor, the return value depends upon the dwFlags field. The flags from MonitorFromPoint are used. If the window intersects more than one monitor, this returns the monitor containing the majority of the window. |
![]() |
| The same goes for GetDeviceCaps(hdcPrimaryMonitor, HORZRES/VERTRES). If you use SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN, SM_CXVIRTUALSCREEN, or SM_CYVIRTUALSCREEN, you get the left, top, width, and height of the virtual screen in pixels, respectively (see Figure 6). The SM_SAMEDISPLAYFORMAT returns true if all monitors have the same color format. Note that two displays can have the same bit depth but different color formats if the red, green, and blue pixels have different sizes or are located in different places in a pixel. SM_CMONITORS tells you how many monitors are on the desktop. |
Print("SM_CMONITORS is %d", GetSystemMetrics(SM_CMONITORS)); |
| would produce the following output on my system: |
SM_CMONITORS is 2 |
| As you can see, my sample system has two monitors, both using the same pixel color depth. One of my monitors is 1024X768 pixels and the other is 800 X 600 pixels. The 800 X 600 display is on the left, the 1024 X 768 display is on the right, and their bottom pixels are aligned. PRIMARY. szDevice is the name of the device, and it is only present in the MONITORINFOEX struct. Most apps will never use this field, and can pass in a MONITORINFO struct instead of a MONITORINFOEX. For example, this piece of code |
|
| produces the output: |
|
as good as GDI can do by itself. But if an app wants to, it can paint optimally for the particular display using cus- tom algorithms smarter than GDI's defaults. In the API declaration |
|
hdc is an HDC with a particular visible region. The hdcMonitor passed to MonitorEnumProc will have the capabilities of that monitor, with its visible region clipped to the monitor and hdc. If hdc is NULL, the hdcMonitor passed to MonitorEnumProc will be NULL. lprcClip is a rectangle for clipping the area. If hdc is non-NULL, the coordinates have the origin of hdc. If hdc is NULL, the coordinates are virtual screen coordinates. If lprcClip is NULL, no clipping is performed. |
|
where hmonitor is the monitor. The callback is called only if it intersects the visible region of hdc and is non-NULL and lprcClip is non-NULL. hdcMonitor is an HDC with capabilities specific to the monitor and clipping set to the intersection of hdc, lprcClip, and the dimensions of the monitor. If hdc is NULL, hdcMonitor will be NULL. lprcMonitor is the clipping area that intersects that monitor. If hdcMonitor is non-NULL, the coordinates have the origin of hdcMonitor. If hdcMonitor is NULL, the coordinates are virtual screen coordinates. dwData is application-defined data that is passed in EnumDisplayMonitors. |
|
To paint the top half of a window using the capabilities of each monitor, an app would write the following: |
|
To paint the entire screen using the capabilities of each monitor, the app would call: |
|
To get information about all the displays on the desktop, the app would call: |
|
The EnumDisplayDevices API allows you to determine the actual list of devices available on a given machine: |
|
lpReserved is reserved for future use and must be zero. iDeviceNum is a zero-based index on the device from which you want to retrieve information. pDisplayDevice is a pointer to a DISPLAY_DEVICE structure for the return information. dwFlags must currently be zero. The DISPLAY_DEVICE structure looks like |
|
where the state flags are defined as |
|
Here's more sample code |
|
and the output it produces: |
|
|
|
Assuming that you cannot use the DS_CENTER window style (which is really the best way to center a dialog), you could try something similar to the code in Figure 8. CYSCREEN are now going to return the x and y resolution of the primary monitor, and SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN, SM_CXVIRTUALSCREEN, and SM_CYVIRTUALSCREEN are provided to get the origin and extent of the virtual desktop. Keeping that in mind, you can see that the code in Figure 8 is functionally equivalent to the code above for systems with only one monitor.
|
|
should be: |
|
Better still, use the new macros provided in the SDK in Windowsx.h: |
|
|
As you can see, this only blanks the primary monitor. It has been updated and now does this: |
|
This gets the RECT of the virtual desktop. This method will work correctly on Windows 95, Memphis, Windows NT 3.1, and Windows NT 4.0, so applications linked with the new Scrnsave.lib will also work on those systems. |
![]() |
MULTIMON_STUBS. This file actually contains code and, therefore, must be included in only one module or it will generate "multiply defined" errors at link time. You should define this constant in one source file before including this include file; you should not define it in any other source files that require you to include this file.
|
![]() |
![]() |
|
Terminology
The following terminology for multimonitor support is currently used in the Windows documentation. VGA monitor This is the main monitor that you see text on when the computer initially boots. It's also the monitor that DOS apps will run on when running exclusively (in DOS mode) or when they are running full-screen. Primary monitor Another name might be the compatibility monitor since this is the monitor that is guaranteed to contain traditional screen coordinates. Almost all multimonitor-challenged apps will run correctly on this monitor. The primary monitor may or may not be the VGA monitor. The VGA monitor is determined by the system because of its location on the bus. On the other hand, Windows determines the primary monitor. Secondary monitors These are all of the monitors that are not the primary monitor but are included in the Windows desktop area. Independent displays These monitors are present on the system, but are not part of the Windows desktop. These monitors can still be used by applications, but they do not contain any part of the Windows desktop. The calculation of the virtual desktop does not include this area and, as a result, you cannot drag application windows to or from this monitor to other monitors on the system—even if an application is running that makes use of this monitor. Mirrored monitors These are monitors that are present on the system that receive a duplicate of all activity on the primary monitor. This allows a user to present the same information on multiple displays. A user can configure devices to be mirrors using the control panel. (There aren't really any development aspects to this type of monitor, but it's worth knowing about.) |
Get it at your local newsstand, or better yet, subscribe. |





