What’s so special about bitmaps and DCs?

Date:May 1, 2006 / year-entry #151
Tags:code
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20060501-34/?p=31353
Comments:    31
Summary:You can select pens, brushes, fonts and bitmaps into a DC with the SelectObject function, and from this list, bitmaps are special. Because, if you look carefully, bitmaps are the only modifiable objects on the list. Pens, brushes and fonts cannot be modified once they are created. But bitmaps, oh, bitmaps. A bitmap selected into...

You can select pens, brushes, fonts and bitmaps into a DC with the SelectObject function, and from this list, bitmaps are special. Because, if you look carefully, bitmaps are the only modifiable objects on the list. Pens, brushes and fonts cannot be modified once they are created.

But bitmaps, oh, bitmaps. A bitmap selected into a DC changes as you draw into it. Selecting a bitmap into multiple DCs means that writing to the bitmap from one DC secretly changes it in another, which isn't a very nice thing to do to a DC.

So let's see, you can select pens, brushes, and fonts into multiple DCs, but you can't do it with bitmaps. Coincidence? You make the call.


Comments (31)
  1. asdf says:

    except for the stock bitmap.

  2. Sean W. says:

    Frankly, I think the behavior of SelectObject is really kind of strange all around, and stems from an era where API designs weren’t planned around type-safe languages:  SelectObject looks strikingly like it was designed to be called from assembly language.  In a type-safe language, there’d be separate functions named SelectPen and SelectBrush and SelectBitmap for this, and, in fact, having designed it that way would have probably made GDI more efficient:  It should be fairly obvious that somewhere deep inside SelectObject there’s a big switch() statment to handle all the different kinds of things it can be given, or a call-by-pointer — both of which would be unnecessary code with separately-named API calls.

    Worse, though, this one-size-fits-all approach pervades Windows’ entire design:  Consider the semantics of CloseHandle or GetStockObject or even SetWindowLong/GetWindowLong.  For all of these to work correctly, there must be a big switch statement in there or a call-by-pointer (or, in the case of SWL/GWL, if no switch statement, some serious parameter-validation code).  These switch statements are, effectively, useless computation:  Clock cycles that must be wasted solely because of the API design.

    But, alas, what’s done is done, and Windows’ design remains weird, with a lot of lingering quirky special cases like this one.  But there’s hope on the horizon:  The API designs <b>will</b> be better in Vista, won’t they, Raymond?

  3. mschaef says:

    "Selecting a bitmap into multiple DCs"

    I know it’s bad form, but is this even legal under Win32?

  4. Dean Earley says:

    mschaef, isn’t that the whole point of this post?

  5. KiwiBlue says:

    SeanW wrote:

    "It should be fairly obvious that somewhere deep inside SelectObject there’s a big switch() statment to handle all the different kinds of things it can be given, or a call-by-pointer — both of which would be unnecessary code with separately-named API calls."

    Ok, so how much performance would you gain by eliminating the indirect ("by-pointer") call?

  6. cvbcvb says:

    plz don’t touch CloseHandle it goes out from nice design of object-oriented NT kernel and has no realtionship with weaky user32 sbusystem (that placed in win32k.sys). Thereis no switch inside of CloseHandle. Thereis NT object manager that looks appropriate object in its table and calls callback routines to close it (really there was developed C analogue of virtual objects inheritance).

  7. cvbcvb says:

    Firstly. Go away from win32 for a bit of time.

    CloseHandle calls ZwCloseHandle routine that closes all kinds of NT kernel objects. It means – files, security descriptors, synchronisation objects, registry keys (yeah, advapi32 sometimes gives you fake handles that are not really kernel objects handles, both as FindFirstFile gives you some userspace stuff pointer that can be closed only by FindClose, that internally cleanup this stuff and calls ZwCloseHandle).

    NT layer API are very generic and logical. Its design more better than win32 on my point of view. The crookedness starts immideately after ntdll.dll code and gets its maximum in user32 :)

    The reason why you can’t pass GDI/USER handles to CloseHandle is a GDI/USER subsystems didn’t originally written to be kernel-level code and don’t interract with kernel object manager. Obviously there was a war between NT kernel team and team that put user subsystem into kernel space :)

  8. cvbcvb says:

    btw i’m still doesn’t understand why FindClose was so neccessary :. I can implement FindFirstFile..etc using only nt api without keeping intermediate data in userspace.

  9. cvbcvb says:

    OK. Win32 API has poor design, I agree :)

    But root@evil is that this API consists of many different onceptual parts that "tried" to interrogate each with other.

    PS About linux’s kernel – it uses same pseudo-virtual inheritance when handling close (and other calls) ;)

  10. alexandre.r. says:

    cvbcvb wrote:

       NT layer API are very generic and logical.

       Its design more better than win32 on my

       point of view. The crookedness starts

       immideately after ntdll.dll code and gets its

       maximum in user32 :)

    In other words, the "quality" of the design of an API is inversely proportional to the number of people that uses it. I don’t believe this to be a coincidence…

  11. cvbcvb says:

    "generic and logical" not always simple in use. UNICODE_STRING, IO_STATUS_BUFFER and 5+ parameters in functions are not very friendly… But all other API are based on them (not including user32 calls).

  12. cvbcvb says:

    wow. try

    #define CloseKernelObjectHandle CloseHandle

  13. josh says:

    The problem isn’t with CloseHandle.  The problem is with functions handing out handles that can’t be closed with it.  Or perhaps that there is no way to create a CloseHandle-able handle is user space?

    Or would you also complain about IUnknown::Release?

  14. Sean W. says:

    KiwiBlue:  You only gain a few clock cycles by eliminating the indirect call (although there might be a slightly higher gain on modern processors, since the branch-predictor wouldn’t get so confused by it).  Some might say, "Well, so what?  It’s only a few clocks."  But this is a circumstance where the clock cycles didn’t *need* to be wasted; an alternate design could’ve avoided that loss *and* been easier to use and to understand.  That said, that’s a few wasted clock cycles for almost every single WM_PAINT message, and that adds up over time, particularly with modern opaque-dragging and opaque-resizing techniques.

    cvbcvb: Remember that "nice" is in the eye of the beholder.  NT is an object-oriented design built on top of a non-object-oriented language (C).  C code should use C techniques; and C++ code should use C++ techniques.  When you have conceptual overlap, you get messes like NT handles or Xt widgets.  CloseHandle, by attempting to be all things to all handles, falls into the peril that programmers can assume it really *is* all things to all handles, and try to use it to close a handle that doesn’t work.  You can’t feed it any handles from USER or GDI or ADVAPI, and there are even kernel objects that can’t be destroyed with it (hence the need for functions like DeleteCriticalSection, DeleteAtom, and HeapDestroy).  Having a function like CloseHandle should really be an all-or-nothing proposition in framework design:  Either design the function to be a true abstract multimethod that any object can take advantage of, it shouldn’t be included at all, and there should be separate private methods for each type.  Anything in between might *seem* elegant, but it frequently just engenders confusion among lesser-skilled programmers.

  15. KJK::Hyperion says:

    cvbcvb: FindClose is required because FindFirstFile opens a handle to the directory, and FindNextFile needs it open

    Sean: the fact that certain handles cannot be passed to CloseHandle should alone be a big hint that they aren’t "kernel" objects. Also atoms aren’t objects, they are properties of a single object (the handle table), inaccessible from user mode. And Win32 has lived quite fine without an NT kernel for years, thank you. It’s rather NT that picked up bad habits from Win32, I’d say – OS/2 looked much more "NT-ish" than Win32

    josh: the Toolhelp32 API is the only user-mode API that creates handles that can be closed with CloseHandle. The trick is simple: the handle refers to a section object, that contains all the data that was captured in CreateToolhelp32Snapshot. The drawbacks to this approach compared to simply allocating heap memory and returning its pointer as a "handle" are added complexity and a lot of overhead in terms of memory usage

  16. Sean W. says:

    cvbcvb:  I *am* looking at it from a non-Win32 perspective; I’m comparing it to a dozen other operating system designs, as well as dozens of frameworks in a dozen different languages, and ot framework design in general.  An OS API is a framework, and as framework designs go, Win32 as a *whole* is not that impressive.  You can’t separate the NT kernel from Win32 — yes, you can do it programmatically, but the two products are never shipped separately, and their manufacturer would like us to think of them as a single unit (hence the very large public documentation on Win32 and the very minimal public documentation on the NtXxx and ZwXxx functions).  The API that is exposed is Win32, so that’s the one I’m commenting on; whether the underlying implementation of CloseHandle() is NtCloseHandle() or a Linux kernel’s close() or a Mach kernel’s close() isn’t really relevant, because that’s not the exposed API.  Regardless of the underlying implementation, the exposed API is not clean and orthogonal:  There are a hundred types of handles in Win32, and CloseHandle can only deal with about a dozen of them.  That’s not a very good design.

  17. Sean W. says:

    I didn’t say that pseudo-virtual inheritance is necessarily wrong:  Just that it’s often (usually) misused.  In the case of Win32, if you’re going to have a function named CloseHandle(), its name strongly implies that it closes handles — *all* handles.  A Un*x-flavored close() system call can close any file descriptor, no matter where it came from.  Granted, FDs share a smaller scope than Win32 handles do, but you can use a close() system call to close just about anything that the kernel can open, be it a file, a network port, or a serial connection to a toaster oven.  I’m a strong proponent of naming functions after what they do, and close() closes — just about anything, as its name implies.  CloseHandle() closes — but it only works on about one out of every ten handles you encounter, which I consider a pretty severe design flaw.  If it was only supposed to close kernel handles, it should’ve been named CloseKernelHandle().

    (In the meme of naming functions after their actions, what does this imply SelectObject would "naturally" do?  Near as I can figure, it would be a function that would award a prize to the best chunk of code and data given to it…)

  18. KJK::Hyperion says:

    By the way, I think you can indirectly select the same bitmap in two DCs, or even share the a bitmap between processes, by creating two different DIBs from the same section (or two sections mapping the same area of the same file, or whatever level of indirection at which GDI stops caring). Sharing video surfaces seems to be a big taboo, IIRC it’s still not "legal" in DirectX 10 (which introduces virtual video memory)

  19. Dean Harding says:

    Sean W: I would say that CloseHandle is a BETTER name than the Unix "close". CloseHandle at least implies that it closes *handles*, whereas what does "close" close? Files? sockets? Doors? Books?

    You argue that CloseHandle should be named "CloseKernalHandle" (because that’s what it does), but wouldn’t that logic also say that "close" should be called "close_file_descriptor"?

    Oh, and having one entry-point do multiple things saves space in your DLLs import table, which in turn decreases your working set. That may give you better performance than avoiding some imagined "call-by-pointer" (which any call into a DLL is essentially, anyway)

  20. Sean W. says:

    Dean: CloseHandle() is a better name than close() in some ways, but it’s still not descriptive of its actions.  And close_file_descriptor() (or, better yet, close_fd(), since most Un*x programmers know quite well that "fd" means a file descriptor) might be a better name for the similar Un*x kernel function.  That said, the scope of close() is the system kernel, so it’s reasonable to expect that it applies to kernel data and to no other data (whereas the scope of CloseHandle() is that of all of Win32, including at least KERNEL/USER/GDI/ADVAPI and possibly a few other DLLs and therefore ought to apply to all handles managed by these same DLLs).  So the claim of whether close() is a better name than CloseHandle() (or vice versa) is a little debatable, but I think we can unanimously agree that they’re *both* bad names.

    I would say that saving your working set a kilobyte or two is a poor excuse for using an otherwise poor design.  Memory is cheaper than clock cycles, and getting cheaper still as every day goes by.  But a good design beats memory, clock cycles, disk space, or that most scarce of commodities, pixels, and *design* is the sole reason for the success of higher-level Algol-flavored languages like Java and C# and of scripting languages like PHP and Python and Ruby.  These environments don’t concentrate as much on saving every single byte as they do on being appropriate for the tasks to which they are put, which is a smarter model to follow in the long run.

    A call into a DLL *is* already a call-by-pointer, as you claim, which is exactly why you want to avoid doing *another* one on top of that:  A single call-by-pointer should always be sufficient to fully direct a "message" to the recipient code (in message-passing terminology).  If it isn’t, you likely have an overly-general design or one that’s overcomputing to accomplish its task.

  21. Sean W. says:

    cvbcvb:  Here’s an exercise for you:  Provide three different reasons why #defining an alternate name is an extremely bad idea.

    KJK: "…the fact that certain handles cannot be passed to CloseHandle should alone be a big hint that they aren’t ‘kernel’ objects."  This is not a very well-considered statement; let me propose this as a thought experiment, and maybe you’ll see why:  Let’s say that Microsoft introduces two new handle types, named HA and HB.  One of these is closable by CloseHandle(), and one of these is closable by a new CloseUser32Handle() function in USER.  Now here’s your exercise:  Explain which handle must be closed by which function, using only the given information above.  And then on the basis of your answer, explain why this thought experiment is relevant to the existing handle types in Win32.

  22. Nick Lamb says:

    “what does "close" close? Files? sockets? Doors? Books?”

    It closes anything you can open() or dup() and by extension anything that is a file descriptor.

    Files? Yes; sockets? yes; doors? no, you can’t open() them either AFAIK (Solaris feature) and no, Books seem like more of a VMS thing.

  23. Chris Becke says:

    Why should API’s be designed around type safe languages? Non type safe languages exist and are still sometimes needed, and type safe languages can wrap up the unsafe APIs all they want.

  24. KJK::Hyperion says:

    Sean, I really don’t see the point. To me it seems a very petty and irrelevant point, the kind of thing you complain about while sitting on a high throne of never-having-written-an-operating-system

    The fact that a handle is closable by CloseHandle tells you everything you need to know about that handle under a resource management point of view (when closed, a kernel object is dereferenced, possibly freed; can be duplicated; etc.). To introduce "type-safe" wrappers would only introduce noise, because there’s no "type safety" concerning kernel objects and resource management: it works the same for all kinds of them

    Also, see the STRICT #define and the DECLARE_HANDLE() macro. That’s one way type safety is enforced. The other way is that a pointer cannot possibly be a (kernel) handle, because handles have 16 significative bytes (13, actually), i.e. they are always < 65536 (except on Win64, where the kernel actually has enough address space to make a larger handle table meaningful), and no valid pointer can have a value in that range (unless you explicitely allocate memory in the reserved 0-65535 range, but that’s a case of shooting yourself a full clip in the foot with a sniper rifle)

    Finally: GDI handles are not meaningless random values. They are encoded pointers. The encoded value contains, among others, the type of the object pointed and the offset in the shared object table. Also I think that they always have at least one of the "tag bits" set, i.e. the lowest three bits, which are never set in kernel handles. You have to try really hard to pass an "invalid enough" handle to any of those functions

    In general, you are completely pissing over history, conveniently forgetting where does this stuff come from and for how long it has been around, and conveniently ignoring type-safe "wrappers" (silly rabbit, everything is a "wrapper"), and conveniently ignoring other much worse instances of type-unsafe APIs (see lParam & wParam) which you can’t criticize as easily

  25. Sean W. says:

    Chris: Why not design for the highest level of safety in your interface?  Observe how MFC wraps SelectObject() and you’ll see part of the answer to your own question:  If you want a type-safe interface, you should design one; you shouldn’t assume somebody will add one on later, because the add-on is likely to be hacky and buggy.  Ever heard of defensive coding?  Defensive design is the next practice up the ladder, and just as important.  It’s very easy to pass a generic HANDLE to SelectObject() and get a weird, unexpected, or meaningless result.  But it’s much harder to pass a generic HANDLE to a theoretical SelectPen() function, no matter what your language:  Even in a type-unsafe language like K&R C, it’s pretty easy to see you’re doing the wrong thing by calling SelectPen(black_brush) and the right thing by calling SelectPen(black_pen).  It’s much harder instead to see at a glance if your code is right or wrong if it says something like SelectObject(RGB(255,0,0)) or CloseHandle(hWnd).  If you design your APIs to prevent their users from making mistakes, you’ll discover…  that they *will* still make mistakes — but it’ll be far, far fewer!

  26. cvbcvb says:

    About type-safety: of course it is(was) possible to design type-safe system API. But naturally, this typesafety would be lost somewhere on the way inside of OS. Since providing completely-independent mechanism for handling different objects will leave to syntaxically overheaded code. And such design is extremely difficult to extend. So the only problem is to provide documented type-unsafe API or leave it hidden (and possible mutable) inside of OS and provide some layered over it type-safe API.

    About #define … . It was only joke :). But in every joke thereis a piece of truth:

    winbase.h:

    #ifdef UNICODE

    #define CreateFile  CreateFileW

    #else

    #define CreateFile  CreateFileA

    #endif // !UNICODE

    ;)

  27. Simon Cooke says:

    Sean, you’re getting your knickers in a twist over an API which was designed 20 years ago, possibly longer.

    By the way, 20 years ago, that working set issue *was* important.

  28. Sean W. says:

    KJK: This isn’t a question of age.  I know how old the API is and that at its inception it was built for very different hardware.  But choice of hardware is still no excuse for poor design.  The designers, in the 1980s, could very easily have replaced SelectObject with a set of five or six separated entry points named SelectPen, SelectBrush, and yes, SelectTargetBitmap.  (Notice that by writing it as "TargetBitmap" you immediately identify it as having different semantics than the other Select functions.)  CloseHandle could easily have been named CloseKernelHandle when it was exported to Win32 in the early ’90s; or, alternatively, a RegisterHandle callback function could have been added to allow user handles to be registered so that all HANDLE objects could be safely closed by a generic CloseHandle function.  Admittedly, using RegisterHandle to register every new handle solely so that a uniform CloseHandle could close it is probably wildly inefficent, so the better solution is probably just to rename CloseHandle to CloseKernelHandle.  Still, it *could* have been built that way.

    All of this could have been done — even given the limitations of the day — and it was not.

    And please don’t make claims about what I’ve done in the past without researching them first, okay?  You don’t know how many millions of lines of code I’ve written or on what projects.  I do not accept the premise that simply because Microsoft designed it, it is inherently the best design.  Some of their designs are good, and some are bad.  I would encourage you to think critically about them and make a reasonable attempt to sift the wheat from the chaff.  Raymond has challenged all of us to do that here, quite regularly, and in some cases, a reasonable conclusion must be that Windows got it right; and in some cases, a reasonable conclusion must be that Windows got it wrong.

    Simon: You make a good point about the age of the API.  It *was* built under different design constraints, although I don’t necessarily buy that 100 additional bytes spent on the GDI export table and an extra 1K of code would have spelled the immediate flaming death of Windows 2.0.  And yes, I know it’s been the way it is for sufficiently long that it’ll probably never change.  Even so, though, that still doesn’t mean we shouldn’t attempt to analyze the design to find its qualities and its faults; far too much synthesis happens in this industry and far too little analysis.  Good science should have both.

    ———-

    And I’m not even going to *try* to talk about the design nightmare that is wParam and lParam.  That topic’s worthy of an entire book.

  29. Daniel says:

    The kernel handle doesn’t seem like a problem.  What would be a small useful change (especially for newcomers) would be to have documentation (at least) explain when a HANDLE is a kernel HANDLE, a gdi HANDLE, or just some random pointer.

  30. 8 says:

    I really don’t have much trouble with keeping my handles seperate. After all, I requested them, so I know how to close them properly. It "conveniantly" says so in the MSDN docs. Also, how can you confuse a HANDLE with an HDC or GDIOBJ fcol.

    IMHO more diving into the details of the implementation then neccessary will result in more breakage, and maybe Raymond’s hair-pulling getting stronger and stronger. His insightful articles on how programs break and he had to fix it in Windows are here for a reason -_-

  31. required says:

    you can select pens, brushes, and fonts into multiple DCs, but you can’t do it with bitmaps

    "Can’t" meaning "must not", "cannot" [i.e. it is actually impossible], or "weird sh1t will happen if you do"?

Comments are closed.


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index