What are the dire consequences of not selecting objects out of my DC?


The convention when working with device contexts is to restore them to the way you found them. If a drawing function selects a bitmap into a device context, then it should select the original bitmap into the device context before returning. Same for fonts, pens, all that stuff.

But what if you decide to violate that convention? For example, maybe you create a memory DC, select a bitmap into it, and just leave the bitmap selected there, selecting it out only when you get around to destroying the DC. Is that really so bad?

It sort of depends.

The danger of leaving objects selected into a DC for an extended period of time is that the owner of the object won't be able to destroy the object, because you can't destroy objects while they are selected into a DC. For example, if you select a font into a DC, and somebody tries to destroy the font, the Delete­Object call will fail, and you end up leaking a font.

Bitmaps can be selected into only one DC at a time. If you select the bitmap into your DC and just forget about it, then the owner of that bitmap won't be able to select it into any other DC.

Now, if the objects you are selecting into the DC are all under your control, then you can leave them selected into your private DC, because you will know how to get them out if you need to.

Remember that this "leave it lying around, I'll clean it up later" technique requires you to control both the vertical and the horizontal. We've been discussing what happens if you select an object that somebody else controls into your private DC and leave it there. Conversely, if you have a bitmap that you control and leave it selected into a DC that you don't control, then you've got the same sort of problem in reverse: You won't be able to select the bitmap back out of that DC when you need to, because you lost control of the DC.

Bonus chatter: "I've noticed that sometimes, Delete­Object claims to succeed even though it actually failed because the object is still selected in a DC." The GDI folks found that a lot of people mess up and try to destroy objects while they are still selected into DCs. Failing the call caused two categories of problems: Some applications simply leaked resources (since they thought they were destroying the object, but weren't). Other applications checked the return value and freaked out if they saw that Delete­Object didn't actually delete the object.

To keep both of these types of applications happy, GDI will sometimes (not always) lie and say, "Sure, I deleted your object." It didn't actually delete it, because it's still selected into a DC, but it also ties a string around its finger, and when the object is finally deselected, GDI will say, "Oh, wait, I was supposed to delete this object," and perform the deletion. So the lie that GDI made wasn't so much a lie as it was an "optimistic prediction of the future."

Comments (10)
  1. Tergiver says:

    So I wasn't hallucinating, there really is a deferred deletion mechanism for objects selected into a device context! I've been wondering about this for years as I've seen production code that deletes objects that are still selected and yet don't leak GDI objects. In fact you can find an example of this in the MSDN library:

    support.microsoft.com/default.aspx

  2. Joshua says:

    Bonus chatter: Objects are now garbage collected in the case of left behind in the DC. This is topologically equivalent to deleting a file when a handle is open on Unix systems. Unfortunately the caveat "sometimes" makes this unsafe to depend on.

  3. Ooh says:

    Wow, "optimistic prediction of the future", I like that one!

  4. Mike Dimmick says:

    Actually, checking the Application Compatibility Toolkit's Compatibility Administrator reveals there is a compatibility fix (shim) called 'EmulateDeleteObject', whose description is:

    "This compatibility fix causes the Windows XP version of DeleteObject to return success, which is the Windows 9x behavior. Applies to: Windows 95, Windows 98"

    To me that would indicate that 'sometimes' means 'if this shim is enabled'.

  5. Adrian says:

    Raymond wrote: "For example, if you select a region into a DC, and somebody tries to destroy the region, the Delete­Object call will fail, and you end up leaking a region."

    I always thought regions were special cases.  From MSDN:  "Only a copy of the selected region is used. The region itself can be selected for any number of other device contexts or it can be deleted." (SelectClipRgn).  The documentation for SelectObject isn't quite as explicit, but it does seem to suggest that regions are special since the return value is not the handle of the previously selected region.

    [You're right. Regions are special and do not go through SelectObject. I revised the article to use fonts instead of regions. -Raymond]
  6. Myria says:

    Honestly, my perspective on software design is that this "lying" done by DeleteObject is how it should work in the first place – i.e., reference counting.

    Of course, it still means that such programs aren't following the rules as documented.

  7. JM says:

    @Mike: All it means is that according to the description of the shim, on Win 9x, DeleteObject() always returned success whether it succeeded or not, and on Windows XP it does not. (No judgement on whether the description is accurate.) No reason to go off and assume things. Why couldn't DeleteObject() lie without this shim?

  8. Mike Dimmick says:

    For Windows CE users: on Windows CE, GDI does *not* garbage-collect objects. If an object is referenced when you try to delete it, DeleteObject returns FALSE. When the DC is deleted, the object is leaked.

    I'm sure I recall reading that Windows NT-family reference-counts the objects. It's possible that this was changed some time after Windows XP to some form of garbage collection.

    @Joshua: My reading of that statement is that 'sometimes' refers to it lying about having deleted the object, not about subsequently cleaning up. But I could be wrong.

    [I'm not sure what your model for garbage collection is. How can GDI know when a bitmap handle has become garbage? (Maybe the handle got written to a file for later readback.) -Raymond]
  9. Larry Hosken says:

    I would have figured out it didn't stand for "data center" eventually.

  10. Goran says:

    Euh… So the DC will "not always" delete a still-selected object, but when? We don't really want to know, nor think about that, not if we can help it, I guess. A nice "structured" approach to DC selection is to wrap SelectObject into RAII (the only reasonable, if you ask me).

Comments are closed.