Fran Finnegan
Fran Finnegan is chairman of Finnegan O'Malley & Company Inc., a Windows consulting and contract progarmming firm in San Francisco that is developing E-Mail ManagerÔ, an electronic-mail application.
{ewc navigate.dll, ewbutton, /Bcodeview /T"Click to open or copy the code samples from this article." /C"samples_4}
QI’m trying to BitBlt an image of my window to a memory device context. Everything works fine, except for one glitch: my application needs to include the mouse cursor in the resulting bitmap so that the end user can see where the mouse was. When I get the DC for my window, the mouse disappears, because it isn’t part of my window’s client area; it reappears after I release the window DC. Is there a flag or function call I can use to keep the mouse cursor on the screen while I BitBlt from it?
Jim Masse
via CompuServe
AWhen I posed this question to the author of the Borland Resource Workshop, his solution was simple. Take a photo of your screen, and then scan it back into your PC. But is there an easier way?
No, there isn’t a simple way to keep the mouse cursor from hiding. The cursor is removed to keep the mouse from physically disturbing the screen or window device context while you are painting to it or copying from it. You therefore have to paint the cursor on a memory DC’s bitmap after you BitBlt from the screen.
I used HeapWalk to determine that the standard VGA cursor is 268 bytes long. (Look for a 288-byte nondiscardable cursor resource owned by the DISPLAY module. It’s 288 bytes long because global memory’s granularity is 32 bytes.) You can check to see what size other display drivers use.
The mouse cursor format isn’t readily available, so the usual warnings about version dependencies may apply here. But the code in Figure 1 does work for the WindowsÔ operating system version 3.x. It assumes you understand how cursors and icons are painted, as described in Programming Windows by Charles Petzold (Microsoft Press, 1991). Cursors and icons are basically drawn using two bitmaps, one that is first AND’ed with the screen and one that is then XOR’ed with the screen. That’s how cursors and icons can be any shape.
The code in Figure 1 can be dropped into the MainWndProc message switch of the GENERIC application included with the Windows1 Software Development Kit (SDK). Each time you hit the space bar, an image of the cursor is captured and dropped on the client area right where the cursor is (see Figure 2 ).
Figure 2 An image of the cursor is dropped onto GENERIC's client area.
In Figure 1, the GetCursorPos function finds out where the hot spot of the mouse cursor is located on the screen. The SetCursor function is used twice; once to set the cursor to another cursor, say IDC_ARROW, thereby retrieving the handle to the current cursor, then again to put the original cursor back in place. You now have the handle to the current cursor, which is simply a global memory handle, and its location on the screen. Lock the cursor handle down and you get a far pointer to the cursor header data structure and the two bitmaps that make up the cursor. The cursor information at this point is in a device-dependent format.
The cursor header data structure (12 bytes) looks something like this:
WORDwHotSpotX;// X hotspot coordinate
WORDwHotSpotY;// Y hotspot coordinate
WORDwExtentX;// X dimension of cursor
WORDwExtentY;// Y dimension of cursor
WORDReserved;// ??? (always set to 4)
BYTEucNumberOfPlanes;// color planes
BYTEucBitsPerPixel;// bits for each pixel
The normal VGA IDC_ARROW cursor has the hot spot set to 0,0 and the extents set to 32,32. Since the mouse cursor is monochrome, there is only one bitplane and only one bit for each pixel in the bitplane of each bitmap.
The cursor header information is followed by the AND bitmap, then the XOR bitmap. Each bitmap for the VGA driver (VGA.DRV) is 32 bits wide (4 bytes) by 32 lines long, for a total of 128 bytes. The VGA cursors are therefore 268 bytes long.
The information from the cursor header and a long pointer to each of the bitmaps is passed to CreateBitmap, creating a pair of monochrome device-dependent memory bitmaps equivalent to the cursor bitmaps. Once the two bitmaps are created, you can unlock the handle to the original cursor.
At this point, BitBlt your source image from the screen onto your destination device context. Create a temporary compatible device context, and use SelectObject to attach the AND cursor bitmap to it. BitBlt to your destination context with the SRCAND ROP code, taking into account the hot spot offsets. Next, select the XOR cursor bitmap into the temporary device context and BitBlt it to the destination using the SRCINVERT ROP code. Select the original 1-bit bitmap back into the temporary device context, and use DeleteDC to free the DC. Free up the two bitmaps created from the cursor and you’re finished.
The bitmap in the destination device context now has the source image with the mouse cursor image on it. The code in Figure 1 simply BitBlts the memory DC’s bitmap back to the screen, but you could do other things with it at that point.
In fact, you could use the DrawIcon function to paint the cursor instead of using the painting code in Figure 1. DrawIcon also accepts cursor handles since the internal formats of icons and cursors are almost identical. The display driver essentially goes through this double BitBlt procedure every time the cursor is moved or painted.
QI need to change the appearance of the arrow mouse cursor as displayed in the Program Manager and File Manager. This change is required by several users with special visual tracking needs. As a white cursor, the arrow can be easily lost on a complex screen filled with icons. I believe that this cursor is the IDC_ARROW resource, but I have been unable to locate it in any of the obvious files in the Windows directories. Could you point to the appropriate file?
Cliff R. High
via CompuServe
AThe default mouse cursors are actually stored in the display drivers located in the Windows SYSTEM subdirectory. You need to be aware that mouse cursors are monochrome bitmaps. This means that mouse cursors can’t be in color, but they can be made larger or bolder.
One brute force method for modifying the default cursors is far from kosher, but I’ll describe it anyway. Edit the display driver using Borland Resource Workshop or a similar resource editor that can modify executable files such as VGA.DRV. Make a backup copy of your display driver and tweak the cursor bitmap in the file using a resource editor. Be sure and move the hot spot if needed to match the modified cursor. Exit Windows, rename the original driver to get it out of the way, rename the modified driver to the original name, and restart Windows. The advantage of modifying the driver is that you can easily change all the mouse cursors in the driver. The disadvantage is that this procedure is driver-dependent and would need to be done for each video driver you need to use (EGA, VGA, and so on).
A more general approach would involve writing a little application that modifies the normal mouse cursor at run time. See the previous question’s answer and Figure 3 for an example. The code in Figure 3 can be dropped into the MainWndProc message switch of the SDK’s GENERIC application, just as in the previous answer. Each time you hit the Enter key, the cursor is physically toggled between the arrow and hourglass cursors. Figure 2 also shows the bizarre result of doing this. Since the normal arrow cursor is nondiscardable, it may be modified in RAM after the system has loaded it from the driver. The loaded mouse cursor is a pair of device-dependent bitmaps. You could build a cursor using SDKPaint or a resource editor, then use the LoadCursor function to load the new cursor resource, thereby getting a handle to it in memory. Next, use the SetCursor function to get a handle for the system cursor, and then lock down both cursor resource handles.
Armed with the absolute addresses of both cursors, copy the new cursor on top of the old system cursor. After copying the new cursor, unlock the resources and exit the application. This approach is more complex, but it is more portable since it doesn’t need to modify the driver files.
QMy application draws a data structure that looks like a tree or linked list, in a window. The size of the tree grows dynamically and is so big that it goes beyond the screen. I need to provide scrolling. Obviously, I have to paint the uncovered portion of the window when the user scrolls. If I were dealing with text files, I would read the uncovered portion of the text from the text file and paint it in the window. However, I am dealing with bitmaps: this is like a paint application where the drawing area is much larger than the screen. How do I do this?
My idea was to draw everything into a temporary drawing area (like a memory DC) and display the appropriate part of the bitmap in the window’s client area whenever a WM_PAINT message is received. I tried to do this, but failed. I heard from someone that I cannot have a memory DC larger than the screen. Is this true? My drawing area needs to be much larger (about 5 times) than the screen.
Shravan Arra
via Internet
AThe functions for creating and manipulating device contexts and bitmaps are a bit tricky, but there is nothing to keep you from attaching large bitmaps to a memory DC. Bitmaps had to be smaller than 64KB in Windows 1.x, but later versions of Windows handle larger bitmaps. The code in Figure 4 creates a large bitmap (2KB X 2KB) compatible with the current display. Do all your painting into the memory DC, and BitBlt from this DC into the display DC when you receive a WM_PAINT.
On a 16-color VGA display, this 2KB X 2KB bitmap requires about 2MB. The memory DC is just like the display DC as far as GDI calls are concerned, and the appropriate area of the source memory DC is displayed in your destination client rectangle using the BitBlt function. This kind of program can run very fast in enhanced mode if there is enough memory to keep from swapping the large bitmap to disk very often. You just have to be very careful about which functions you are calling with which object handles (and in which order).
Figure 4 Handling Bitmaps Larger than the Display
// These should be global or static variables.
HDChCoDC;
HBITMAP hBigBitMap, hOldBitMap;
// This block creates a memory DC with a large
// bitmap for use by the painting functions.
// It should be run at initialization time.
{
// Get the current display device context
hDC = GetDC( hWnd );
// Create a DC compatible with the display DC.
hCoDC = CreateCompatibleDC( hDC );
// The new DC has a 1-bit monochrome bitmap attached
// to it. Create a new working bitmap using the
// original display DC handle -- not the new DC.
hBigBitMap = CreateCompatibleBitmap( hDC, 2048, 2048 );
// OK, now we're done with the original display DC.
ReleaseDC( hWnd, hDC );
// Select the new bitmap into the new DC, saving the
// old bitmap handle.
hOldBitMap = SelectObject( hCoDC, hBigBitMap );
}
// When a WM_PAINT is received, BitBlt from hCoDC to
// the display DC.
// When the program is exiting, we need to restore the
// created DC to its original form and then delete it.
{
// Select the 1 bit bitmap back into the DC.
SelectObject( hCoDC, hOldBitMap );
// Delete the big bitmap.
DeleteObject( hBigBitMap );
// Finally, delete the compatible DC.
DeleteDC( hCoDC );
}
QI’ve got a Windows-based program that is always iconized. This program gets a WM_TIMER message about every 30 seconds so that it can wake up and check on a state of MS-DOSÒ. The state is either on or off, so I’ve got a different icon for each state. When the state changes, I want the program to switch icons.
The way I change icons is by setting the class word with the following function calls:
hIcon = LoadIcon( hInstance, “ICON2” );
SetClassWord( hWnd, GCW_HICON, hIcon );
This switches icons, but Windows doesn’t paint the new icon until I pick up the program icon and place it elsewhere; Windows then updates it with the new icon. You don’t know the icon has changed until you move it somewhere else. At first I didn’t have any logic in my code for WM_PAINT messages. (I was hoping Windows would update the icon for me.) I’ve enclosed the code I’ve been using (see Figure 5).
Do you have any ideas on what I’m missing or what’s wrong?
Figure 5 Incorrect Way to Toggle an Icon
case WM_PAINT:
hDC = GetDC( hWnd );
InvalidateRect( hWnd, (LPRECT)NULL, TRUE );
DrawIcon( hDC, 0, 0, GetClassWord( hWnd, GCW_HICON ) );
ReleaseDC( hWnd, hDC );
break;
Shaun Pierce
via CompuServe
AI see a number of problems with your code. First, your program won’t receive WM_PAINT messages when iconized if your window class contains an icon handle. Windows sends WM_PAINTICON messages instead. Second, you shouldn’t be using the GetDC/ReleaseDC functions on a WM_PAINT in any event. The BeginPaint/EndPaint functions are the proper ones to use while processing a WM_PAINT message. They take care of validating the paint rectangle so that you won’t receive more WM_PAINTs. Otherwise, you’ll needlessly get an unending stream of WM_PAINTs. Third, InvalidateRect generally shouldn’t be called for a window when processing a WM_PAINT for that window. The InvalidateRect call inside the painting case statement may cause a WM_PAINT message to be sent again, or may mess up how Windows keeps track of the invalid regions of your window. Fourth, you should avoid unnecessary casts, such as the (LPRECT) on the NULL pointer. Casting when you don’t need to is a bad habit that can hide coding errors from the compilers. Finally, for your application, you really don’t need to handle the WM_PAINT message at all!
Delete the case for the WM_PAINT message from your switch, and simply use the FlashWindow function along with the SetClassWord function in your timer message handler when you need to change the icon, as shown in Figure 6. For an iconized application, a pair of FlashWindow calls will blink the icon, causing a WM_PAINTICON message to be sent through you to the default window procedure (DefWindowProc), thereby causing your selected icon to be repainted automatically. You don’t need to do any painting at all.
Figure 6 Correct Way to Toggle an Icon
case WM_TIMER:
// determine the MS-DOS state
.
.
.
// if the DOS state has changed, do this:
{
// if the DOS state is now OFF, do this:
hIcon = LoadIcon( hInstance, "ICON1" );
// else it's now ON, so do this:
hIcon = LoadIcon( hInstance, "ICON2" );
FlashWindow( hWnd, TRUE );// hide icon
SetClassWord( hWnd, GCW_HICON, hIcon );
FlashWindow( hWnd, TRUE );// show icon
}
break;
QI’m developing a tool that will enable the user to traverse a complicated structure of cascading menus. Currently, after selecting a menu pick, the user is inconveniently returned to the top-level menu instead of remaining at the point containing the option last invoked.
Is there a method by which I could, after an option has been selected, leave the user at that last submenu accessed (possibly highlighting the option last invoked)? That way the user won’t have to plod back through the menus from the very root of the structure.
I would greatly appreciate any comments or helpful hints for overcoming this annoyance.
Piotr Golec
via CompuServe
AThere isn’t an easy way to implement a menu system that keeps the menu pull-downs displayed and updated after selections have been made. The built-in Windows menu system would have to be replaced with your own menuing system to accomplish your goal. I don’t recommend rolling your own menu system because of the effort involved, and because nonstandard menu operation would be confusing to a normal Windows user. Instead I would reconsider the overall design of your application’s user interface.
I suspect that you could reorganize the cascading menus into a collection of high-level menus possibly leading into nested dialog boxes. Check boxes and/or radio buttons would maintain their previous states in the dialogs as required for the convenience of the application user. One of the main menu items might be a Last Dialog function to automatically traverse the saved state variables and bring up the appropriate dialog(s). That (probably) wouldn’t be necessary, if clicking OK or Cancel in each nested dialog simply returned you to the previous dialog.
If you expect a wide range of user levels, you might want to default to a mode that provides a minimum number of likely choices on any given menu and dialog. Hide the more obscure or dangerous options until an expert mode is selected. Be sure to save the appropriate state values, mode choices, and window positioning information in a private profile file. Well-chosen defaults and saved configuration information help make a good program.
Alternatively, if your heart is set on having a menu-like feature, implement an icon bar or tool window. This type of “menu” would be continuously displayed at the user’s option and could accomplish what you want to do.
1For ease of reading, “Windows” refers to the Microsoft Windows operating system. “Windows” is a trademark that refers only to this product.