Some minor changes to ShellExecute and ShellExecuteEx ensure that any spawned applications come up on the same monitor as the parent application. If you specify an hWnd when calling ShellExecute or ShellExecuteEx, the new application window will appear on the same monitor as the window referred to by the hWnd.
The sample program (see Figure 10) simply calls the new APIs to get information on the system configuration and dumps out that information. The code was built with the multimon.h header file so it works on single-monitor systems as well as on Windows 95 and Windows NT 4.0.
Let's walk through some of the sample code, starting with multimon.h and then Figure 5. I'm going to skip over most of the constants and structure definitions of multimon.h since they're pretty self-explanatory. I'll start with the section labeled (via comments) "Implement the API stubs." Notice that just before this comment is an #ifdef COMPILE_ 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.
If COMPILE_MULTIMON_STUBS is defined, then the code that follows in the header will be included. This declares a number of global function pointers that will be used by the stub code to locate the corresponding APIs built into the operating system, if present.
Looking at the first function, InitMultiplMonitorStubs, will clarify things somewhat. It should be called once before any of the APIs defined in the header can proceed, although you don't have to worry about it since the included API stub code calls it as necessary. This function determines if the underlying operating system has built-in support for multiple monitors. If it does, then it gets the correct addresses for the APIs (in the system file USER32) that correspond to those in this header file and initializes the global function pointers appropriately. If the underlying operating system doesn't support multiple monitors, then these pointers are set to NULL. In either case, the function sets a static flag that indicates whether this API was correctly initialized, and returns TRUE on a system that has built-in multimonitor support or FALSE on a system that does not. (You can see this in the very first if construct: if this function was already called, the function quickly exits using one of the function pointers to determine if there is built-in support on the platform.)
At this point, the file moves into stub function implementations. Since many of the stubs are implemented in the same manner, I'll cover only a few in detail. The first real API stub is the GetSystemMetrics function. Notice that the name of the stub is actually xGetSystemMetrics. This allows you to enhance or replace the underlying operating system API without getting compile errors by later redefining GetSystemMetrics as xGetSystemMetrics (see the #defines at the end of the include file).
First, the xGetSystemMetrics implementation makes sure InitMultipleMonitorStubs code was called to initialize things and to determine if you're on a multimonitor-aware operating system. If you are, control is passed directly to the operating system's implementation of GetSystemMetrics. If your system isn't multimonitor-aware, then review the flags, handle those that would not have been recognized on a system without multiple monitors, and return appropriate values (knowing that on such a system there will be only one monitor and that it'll have the standard coordinates). Finally, if the flag is not one of the new ones, you pass control to the operating system for handling as usual. This is a general theme throughout the stubs.
TestMM.C is a basic Windows application with many familiar features, including WinMain with a message loop and some support routines to allow easy printing into the main client area. It also has some standard menu items for executing API calls that illustrate the effect of using those APIs on the application window.
The interesting portion of the application is actually the DoTestMM function, which is called whenever the application window is moved or the user presses F5 (the standard refresh key). It is called every time the application is moved so that it can reprint information specific to the monitor the application is actually displayed on. Notice that the DoTestMM function includes a check that quickly exits if it's on the same monitor as it was the last time it was called (since none of the information would have changed in that case). From that point on, the code just calls the various multimonitor APIs and prints the resulting information into an edit box created and sized to fill the client area of the application window. Although not a very exciting application, it does show how to call the various APIs, their relevant structures and flags, and how to use them, as well as what to expect in response to those APIs.
Finally, the listings include an MMHelp file that contains routines that handle common tasks on a multimonitor system. Although the routines are fairly simple, I'll quickly review some of them here.
GetMonitorRect is used to determine the screen or work area, depending on the flag passed in the third parameter, for a window. This basically passes in the area of the screen that is closest to the window handle. You use this to clip a window onto a visible portion of the screen. The ClipRectToMonitor function uses GetMonitorRect to determine the best monitor to clip a rectangle to, and then returns the updated rectangle that represents the best place to put it. This might be useful if you want to display a dialog and make sure that it's visible. You can pass this function the coordinates that you'd like to use and it will find the closest location where that entire dialog can be visible. In fact, the new function ClipWindowToMonitor does just that. Given a window handle, it gets the bounding rectangle of the window, uses ClipRectToMonitor to find an appropriate location, and then moves the window to that location.
Similarly, CenterRectToMonitor determines the correct monitor on which to center a rectangle and then updates the rectangle so that it is centered on that monitor. CenterWindowToMonitor uses CenterRectToMonitor to determine the appropriate center location and then moves the window to that location. IsWindowOnScreen determines if your window is actually visible anywhere on any screen, and MakeSureWindowIsVisible makes sure your window becomes visible on a screen.
Installing Multiple Monitors
Multiple monitor support is available only on the Memphis and Windows NT 5.0 operating systems that have updated video drivers (that is, updated to support multiple monitors). Any video card that is supported by Windows can be used as your primary display, but the extra cards must have updated drivers to support the new features. Currently, Microsoft has updated drivers for a number of video cards designed for use on a PCI bus that will be shipped with the operating system. (For more details on hardware, see the section on supported hardware in your documentation.) At the time I wrote this article, drivers for the following cards were available:
- ATI Mach64, Rage I, II & III
- S3 764(Trio), 764V+(765)
- Cirrus 5436,7548,5446
- Imagine 128 I and II
- S3 M65
- Matrox Millenium
- Matrox Mystique
- Cirrus Laguna
- ET6000
- Rendition
- 3DFx
- STB
Setup is pretty much plug and play. First, you must get your system working with one monitor. Then, shut down the system and install another video card and monitor. When you restart your machine, Windows should automatically detect the new card (and possibly the monitor). Once the proper drivers get copied to your machine, you can go to the control panel to configure the physical mapping of your virtual desktop to your monitors, as well as the resolution, color depth, and refresh rates for each.
Note that the order of the cards on the bus may impact the configuration. In particular, the VGA monitor where startup MS-DOS text is initially displayed is chosen by the system before Windows even starts. As a result, you may need to rearrange the cards in order to get the configuration the way you want it.
User Interface Changes
One of the nice things that you will notice about adding multiple monitors is that not much has changed in order to provide this support. In fact, if you have only one monitor there will be no noticeable difference on your machine because most of the changes were actually made beneath the surface of the operating system. However, you will notice a few things if you install multiple video cards and monitors.
|