Date: | April 16, 2010 / year-entry #114 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20100416-00/?p=14313 |
Comments: | 25 |
Summary: | A number of stock GDI objects are made available by the GetStockObject function, but one stock GDI object that is mysteriously missing is the stock bitmap. You can't summon the stock bitmap, but it manages to show up in various places, some of them perhaps unexpected. The stock bitmap is a monochrome 1×1 bitmap which... |
A number of stock GDI objects are made available by the
The stock bitmap is a monochrome 1×1 bitmap which GDI uses
in various places where it has to produce a
Every DC and metafile has a current bitmap (which you can retrieve
with
There has to be a bitmap (as opposed to just leaving it // select the new bitmap and save the old one HBITMAP hbmPrev = SelectObject(hdc, hbmNew); ... do something with hdc ... // all done - restore the original bitmap SelectObject(hdc, hbmPrev);
If Normally, a single bitmap cannot be selected into more than one DC, but the stock bitmap has the magical power that it can be selected into multiple DCs at once. Without this magical power, GDI would have to create a different dummy bitmap to select into each newly-created DC and carry it around so that it can be selected back into the DC just before it is destroyed. Seems awful wasteful to allocate an extra bitmap per DC just for this, especially back in the days of 16-bit Windows when GDI heap space was extremely limited. There is one more place (that comes to mind) where the stock bitmap appears, and it's somewhat unexpected:
In other words, if you ask for a nothing-bitmap,
you get the dummy bitmap back.
This is analogous to the case of calling
Note however that this behavior of returning the stock bitmap handle
when asked to create an impossibly thin bitmap
does not apply to the |
Comments (25)
Comments are closed. |
You think it’s just an itty-bitty bitmap good for nuthing? No. It’s a copy of Kazimir Malevich’s picture!
Ah, finaly, comments enabled for this topic! :-)
Got a question. For a case such as
hdc= CreateCompatibleDC(AnotherHdc)
hBitmap = CreateDIBSection(hdc, TempBI, DIB_RGB_COLORS, 0, 0, 0)
hOldBitmap = SelectObject(hdc, hBitmap)
… do something
SelectObject(hdc, hOldBitmap)
DeleteDC(hdc)
DeleteObject(hBitmap)
Do we have a memory leak if we skip the last SelectObject and just delete the DC? Eg;
hdc= CreateCompatibleDC(AnotherHdc)
hBitmap = CreateDIBSection(hdc, TempBI, DIB_RGB_COLORS, 0, 0, 0)
SelectObject(hdc, hBitmap)
… do something
DeleteDC(hdc)
DeleteObject(hBitmap)
In theory you can’t delete a bitmap while a DC is selected into it, but what if we delete the DC first? It doesn’t seam to leak…?
Maybe he does, but I don’t.
There could be two reasons why it doesn’t leak: 1) the bitmap has been automatically deselected so DeleteObject succeeds or 2) the bitmap has been automatically deleted (so DeleteObject fails).
The MSDN documentation for DeleteDC and ReleaseDC is severely lacking and should contain an explanation of the semantics of retiring a device context.
It’s weird that when you ask for an Xx0 or 0xY, you get 1×1. Thanks for saying that this is how it has been designed to work.
I’ll chalk that up as undefined, subject to change without notice. (The relevant MSDN pages should state so.) I always habitually restore the old state of device contexts, but the relevant documentation is at the SelectObject page and I’m not sure it’s quite correct. It says ‘after it has finished drawing’ which probably should be ‘before it deletes or releases the device context’ unless there’s even more going on… but then we’d need a good rule to decide when we’ve finished drawing.
It’s part of the initial state of device contexts, so the question of what happens to a device context that contains something else when it goes bye-bye is natural.
There is an undocumented quirk in GDI on NT systems: you can actually delete selected objects and they will go away as soon as they’re not referenced anymore.
Or am i wrong? I would like to know about those secret rules.
SelectObject also returns NULL on success somtimes, usually when you restore the original objects. The program must not fail if SelectObject returns NULL. The return value is useless. Returning a dummy bitmap is a architectural flaw.
In general, the GDI API for using bitmaps is a mess. It’s more complicated than it has to be, even considering it must work in real mode. 50 lines of unmaintainable code, at minimum, to draw a bitmap in a window really suck.
I beleive this is the answer lag problems with terminal server. The underlying API is unnecessary complex.
[I think you know the answer already. You’re just hoping I’ll say what you want, knowing that I won’t. -Raymond]
Raymond, I really don’t, or I wouldn’t be asking, period. And I’m not alone in not knowing or being confused about this.
IMO, there are two possible things that could leak: the 1×1 ‘stock’ bitmap which is now an orphan (on the other hand, if it’s ‘stock’ and shared it doesn’t leak, which is why you don’t need to delete it either) or hBitmap. The later for sure doesn’t, event though I’ve deleted the DC before restoring it, or I would have a HUGE quickly noticeable memory leak.
What I am asking is if, *in this specific case*, not restoring the DC before deleting it would introduce a very small leak of some kind or not. And if it causes a leak, what exactly is leaking?
[Or am i wrong? I would like to know about those secret rules.]
If you’d like to know about them for the sake of knowledge ok, but since they are undocumented you shouldn’t rely on them in your applications.
"SelectObject also returns NULL on success somtimes, usually when you restore the original objects."
How? When you restore the original objects, it returns the object that you selected into the DC to replace the original objects. It’s not a stack or anything like that, where retuning non-NULL means that you still need to pop something off. The only time it could return NULL on success is if there was nothing selected into the DC at the time you called SelectObject. The only way there could be nothing selected into the DC is if the DC started off with nothing selected for that particular type, you selected a NULL object into it, or somehow the selected object became NULL without the program calling SelectObject. However, passing NULL to SelectObject is an error, because then the function can’t determine which type of object you meant. Therefore, you can’t select a NULL object into it. I don’t know what happens if you try to DeleteObject on something while it is selected into a DC. Does selecting an object into a DC do something to it so that DeleteObject knows not to actually delete it? If so, then this eliminates that possibility for SelectObject to return NULL on success. What other way do you know of for SelectObject to return NULL on success?
"The program must not fail if SelectObject returns NULL. The return value is useless."
How is that?
"Returning a dummy bitmap is a architectural flaw."
It’s not a dummy bitmap, it’s a stock bitmap, just like the stock pen and stock brush that are initially selected into the DC. If you think it is an architectural flaw, then how would you have done it differently?
GDI is indeed more complicated than it needs to be, but I would hardly call it a mess.
Once you come to terms with what a device context really is, its not so bad. I think that most programmers see device contexts as some form of mysterious voodoo.
A device context is a list of handles (to objects, and a driver) used in rendering. Thats it. Thats all there is to them. Simple, right?
The complications come from the fact that while you may or may not "own" the objects in it, you have an absolute responsibility to clean up those objects that you do "own."
Simplified GDI code doesnt pass device contexts around unless absolutely necessary. Such code creates device contexts on-demand and releases them as soon as possible. This code passes the handles to the relevant objects (bitmap, font, pen, brush..) around instead.
Any other methodology risks "container spaghetti" where at certain points in the code it is non-obvious what the state of the container is (who owns each of the things inside), and thus, non-trivial to destroy the container properly. Often within an exception handler it is not at all obvious what the state of the device context is unless the handler is local to its creation and deletion.
[What valid operation on a 0×n or n×0 bitmap is not possible on a 1×1 bitmap?]
0×n or n×0 bitmaps would give more consistent results when querying attributes (GetObject), for example if the bitmap represents the visible part of some graphical object (which may completely hidden). Just like a file of length zero is often useful even if it doesn’t contain any data.
Why not simply SaveDC/RestoreDC ? This way you don’t need to bother with restoring the DC to the initial state, and save all the handles.
Unless it requires a lot of memory.
Sounds like a backwards compatibility hack. What should happen is that the deletion will fail and everything will stay as it was.
It’s about as neat as it reasonably could have been designed in an age where object oriented programming wasn’t commonplace yet. A lot of the Windows API is essentially emulating oop in C. Yes, we can do better now, but I suspect that’s only because we’ve seen C++ and Java.
Thanks for the insight into this mysterious object. A few questions:
(1) What is the (historical) reason for preventing creation of *real* bitmaps with zero width or height? (CreateBitmap returns the stock bitmap and CreateDIBSection fails.)
(2) Is the bitmap returned by CreateBitmap(0,0,0,0,0) a real stock object? That is, is it possible to skip calling DeleteObject, and can it be used to return a (memory) DC to a “clean” state (see http://msdn.microsoft.com/en-us/library/ms969928.aspx)?
(3) If the stock bitmap is the same for all DCs, why doesn’t GetStockObject return it?
"It is a general principle that you need to restore the DC to how you found it when you’re done."
That’s certainly true of DCs obtained via GetDC since they can be shared by multiple windows, but does that really apply to *created* DCs? Aren’t created DCs totally independent of other DCs?
"Here’s what happens: An error occurs."
Can you elaborate? I have never restored the stock bitmaps/pens/brushes/fonts before calling DeleteDC, and haven’t observed any failures, leaks, incorrect painting, etc., on 9x or NT.
(I understand your advice is to "just not do it", but "just not doing it" in my case would require modifying/retesting hundreds of lines of code…)
[Sounds like a backwards compatibility hack. What should happen is that the deletion will fail and everything will stay as it was.]For NT3.1 or some buggy old ms app i guess then? Win16/9x are just failing deletion.
Raymond said:
[Here’s what happens: An error occurs.]
Mx said:
[Can you elaborate? I have never restored the stock bitmaps/pens/brushes/fonts before calling DeleteDC, and haven’t observed any failures, leaks, incorrect painting, etc., on 9x or NT.
(I understand your advice is to “just not do it”, but “just not doing it” in my case would require modifying/retesting hundreds of lines of code…)]
Exactly the same problem here, although very recently I started changing the code so it does restore the DC before deleting it (which is why I am so interested in a clear and detailed response to my question). A lot of work which can introduce some real bugs if I am not careful – and for what?
As someone who has several times explained the hidden costs of fixing a real bug, you know exactly what I mean.
What error occurs? Where? What are the consequences of this error? The documentation says nothing about this, so only an ‘insider’ like you might be able to shed some light on it. If you refuse to, we’re just be left in the dark and nobody will be any wiser: just like the parent who tells his kid not to do something but doesn’t bother clearly explaining to him what the consequences of that action are.
I also understand that you don’t want to encourage the ‘wrong’ way of doing things, but, like Mx, I never observed any immediately visible leak of any kind. Is it such a slow leak or consequence that it would take a very, very, long time to become apparent?
Jorge, maybe you should experiment with the theory that it doesnt leak by throwing your actions into an infinite loop and then watching the resource usage.
This past month I helped a person on a forum with a game loop.
At the beginning. he pretty much wasnt doing anything right and was leaking VERY badly, causing the program to misbehave only after a few minutes, which is why he posted asking for help.
At the end he really wanted me (and others) to bless his not restoring the stock objects, and when I wouldn’t bless that, he wanted to know of ways to create new instances of the stock objects at DC deletion time. I refused to bless that as well.
Restore the stock objects. If its hard to do that, then maybe your design is flawed (why is the DC in an unknown state?)
[Jorge, maybe you should experiment with the theory that it doesnt leak by throwing your actions into an infinite loop and then watching the resource usage.]
And I did just that.
After 1,000,000 interactions creating a 1024×1024 pixel 32 bit bitmap WITHOUT restoring the DC to the stock object before deleting it, there was no memory leak, no Handle leak, no GDI leak and no User leak.
This said, I did notice the following: RAM usage would sometimes jump from 10,000 KB to 14,000 KB and then back down to 10,000 KB (I was running this in the VB5 IDE, by the way).
Either there is no leak, or, if there is one (which might explain the occasional 10,000 KB to 14,000 KB and back again jump), then Windows is cleaning up after me.
Still none the wiser. :-P
Even if your program behaves correctly if you don’t restore device context state, according to Raymond you’re relying on undocumented behaviour that might be subject to change or different among implementations. Maybe this will mean that your program will stop working in Windows 8, or will be hard to port to Wine, ReactOS or Windows CE. Or maybe you can run into an edge case where Windows doesn’t do the right thing and your program screws up.
As for code changes being likely to introduce new bugs, I really doubt that in this case. Your code probably looks like:
|dc = CreateDC(…);
|SelectObject(…);
|…
|SelectObject(…);
|…
|DeleteDC(dc);
You just need to change this to:
|dc = CreateDC(…);
|HGDIOBJ oo = SelectObject(…);
|…
|SelectObject(…);
|…
|SelectObject(dc, oo);
|DeleteDC(dc);
Or use SaveDC and RestoreDC if you’re swapping so many objects around that you’re getting confused. Note that changing your code like this will even make interacting with your own code easier. Suppose you write code like this:
|… draw stuff …
|DrawSmiley(dc);
|… draw stuff …
If the function DrawSmiley leaves the device context in its original state you won’t have to worry about reselecting pens and brushes.
That shouldn’t matter. What does matter is that you a) don’t delete a bitmap while it’s still in use and b) that you deselect your bitmaps properly. Point b) has been discussed into tedium. The basic scenario of point a) is:
|SelectObject(dc, bm);
|… draw stuff …
|Subroutine(dc, bm); //Deletes bm.
|… draw stuff …
This actually bears some resemblance to a common anti-pattern where you’re creating and deleting objects on different scope levels. Similar things apply to all operations where you do two actions that constitute a logical pair, like opening and closing files for example.
Either you move them upward like this:
|obm = SelectObject(dc, bm);
|… draw stuff …
|Subroutine(dc, bm);
|… you can do more stuff here …
|SelectObject(dc, obm);
|DeleteObject(obm);
|… draw stuff …
Or you move them downward like this:
|… draw stuff … //But put everything relating to bm in Subroutine.
|Subroutine(dc, bm); //Selects, deselects and deletes bm.
|… draw stuff …
It should always be easy to pair SelectObjects, logically they’re like { and }. Creation and deletion of non-temporary bitmaps is more like using new and delete. It shouldn’t be any harder not to delete a bitmap that’s still selected than it is not to delete an object that can still be accessed. Not trivial perhaps, but you’re a programmer so as long as your design is good you won’t find it hard and I don’t see how you could introduce bugs.
I *have* been changing the code to restore the DCs to the stock objects before deleting them when I became aware that this could potentially lead to a leak, although nobody seems to know for sure and I have no evidence that indeed it does.
It’s only hard because it’s a HUGE, very graphic intensive, project – so there are a lot of changes to do.
The problem is that I have no confirmation that all this work (which can easily induce bugs if I’m not careful matching everything as it’s tedious work and most objects are destroyed in a difference place from where they are created) is actually benefiting anything!
Unlike what Raymond seems to think, I’m not looking for someone to bless the wrong way of doing things (I have been changing the code just in case, after all), quite the opposite, in fact: I’m looking for a confirmation that the work I’ve been having (and the risk of introducing a serious bug somewhere) is worth it. A definite confirmation of the type ‘yes, it’s worth having all that work because if you don’t you get error <so and so> because of <this and that>, which, on the long run, results in <this> happening’.
So far I got a ‘it results in an error’. Comming from Raymond (thanks Raymond) that’s more information than I previously had, sure, but I’m still none the wiser as to where the error happens, why it happens, and what are the practical consequences of this error.
Hope that clears it. :-)
P.S. I don’t see any GDI or User leaks. As for memory, it would be very hard to detect if the leak(?) is very small. *Apparently* there is no leak.
[Even if your program behaves correctly if you don’t restore device context state, according to Raymond you’re relying on undocumented behaviour that might be subject to change]
Yes, unfortunately I only noticed his reply to Tihiy *after* posting the above. Tihiy’s theory about the ‘undocumented quirk’ is apparently true, but Raymond and you are right in that one should not rely on undocumented behavior (unless there is absolutely no other choice, which is not the case).
Note that sometimes one *does* have to rely on undocumented behaviour when doing something the OS has no API support for – with the knowledge that things might change without notice with a new Windows version or Service Pack and that your program might break or stop working correctly.
As for the code changes, sometimes it’s that simple (i.e.; the bitmap is created and deleted on the same routine), but mostly they’re created and deleted at different places – since there are a lot of persistent bitmaps, it’s easy to make a mistake and mismatch the object names, which would cause a leak, or, worse, a crash.
Needless to say, I’ve been using the ‘correct way’ of doing things ever since I realized there was the potential for a leak, and changing the code to conformity as I run into old routines that do not restore the DC.
Anyway, thanks for the advice! :-)
So the Stock Bitmap is in fact a Null Object:
http://en.wikipedia.org/wiki/Null_Object_pattern
Nice! :)