The Windows Way of Painting
The Windows Way of painting is to beg, borrow, steal, or create a GDI object and then select that object into a device context. GDI objects, like almost everything else in Windows, are identified by handle. These handle types include HBITMAP, HPEN, HBRUSH, HFONT, HPALETTE, and HRGN.
I’ve never found selecting objects into device contexts to be intuitive; I just do it by rote. Let’s go through the process with a bitmap. If you can select a bitmap, you can select anything. First you need an HDC to write to, and then you need the handle of a bitmap. How can you get an HBITMAP? Let me count the ways:
-
You can create one with CreateBitmap or CreateCompatibleBitmap. We’ll do both in a later section, “Inside the Glass,” page 408.
-
You can use the Handle property of a picture object containing a bitmap. Because the Handle is the default member of the picture object and the picture object is the Picture property, you can think of the Picture property as the handle. But if you already have a picture containing a bitmap, you probably don’t need to select it into anything.
-
You can get a handle to a bitmap out of a bitmap resource. You can get it into a picture with LoadResPicture (but then you don’t need it), or you can get it the hard way, as WinWatch does—by digging it out of an EXE file. See “Finding Resources,” Chapter 8.
OK. Assuming that you have an HDC and an HBITMAP, you also need a handle to retain the old bitmap. Even if you created an empty DC and didn’t think you put a bitmap there, you’d better be prepared to get one out and put it back when you’re done. Your code looks something like this:
Private hDC As Long, hBitmap As Long, hBitmapOld As Long
’ Get hDC and hBitmap from somewhere
§
hBitmapOld = SelectObject(hDC, hBitmap)
At this point, if everything goes right, your bitmap appears in the device context. When you’re done, you must clean up by selecting the old bitmap (or other object) back into the DC and then deleting your bitmap:
Call SelectObject(hDC, hBitmapOld)
Call DeleteObject(hBitmap)
I admit that I often ignore the return values. After all, what can you do if your cleanup fails? It’s not supposed to. OK, OK; so I should have asserted:
f = SelectObject(hDC, hBitmapOld)
BugAssert f
f = DeleteObject(hBitmap)
BugAssert f
Is that good enough for you?
Since you don’t always know when it’s safe to destroy the object, you might find it easier to create a temporary DC, select the bitmap into it, copy your temporary DC to the target DC, and then destroy the temporary:
hDCTemp = CreateCompatibleDC(0&)
hBitmapOld = SelectObject(hDCTemp, hBitmap)
’ Copy temporary to destination
Call BitBlt(hDCDst, 0, 0, dxDst, dyDst, hDCTemp, 0, 0, vbSrcCopy)
Call SelectObject(hDCTemp, hBitmapOld)
Call DeleteObject(hBitmap)
Call DeleteDC(hDCTemp)
Now you’re rid of the bitmap and don’t need to worry about selecting it out and deleting it later.
The same process applies to pens, brushes, and fonts, although you’ll rarely need to use them in Visual Basic. The process is similar for palettes, except that you use the specific SelectPalette function instead of the generic SelectObject. If this seems like Greek to you, follow the instructions explicitly and all will be well.
Technically, HMETAFILE, HCURSOR, and HICON aren’t considered GDI objects because you don’t select them as you do the others. But they are in the GDI, they work somewhat like objects, and they are part of the Windows Way. Nevertheless, we’ll ignore them here. I’ll talk about icon and cursor handles in Chapter 8. Other than the sidebar “Metafiles 1, Visual Basic 0,” page 373, you’re on your own with metafiles.