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 every­thing 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:
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.