When will the static control automatically delete the image loaded into it, and when is it the responsibility of the application?
If you create a static control with initial contents(for example, by creating a
ICON control in a dialog template),then the static control will load the contents uponcreation and destroy the contents upon destruction.So at least in the case where you don’t touch thestatic control, things will work automatically.
But once you touch it, things get confusing.
If you send the
STM_SETIMAGEmessage to a static control, this does a few things(assuming your parameters are all valid):
- The previous image is replaced by the new image you passed.
- The message returns a handle to the previous image.
- The static control turns off automatic image deletion.
The third part is the tricky part.If you ever (successfully) send a static control the
STM_SETIMAGE message,then it says,“Okay, it’s all your problem now.”You are now responsible not only for destroying thenew image,but you are also responsible for destroying the old imagethat was returned.
In other words,the following operation is not a nop:
HBITMAP hbmPrev = SendMessage(hwndStatic, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmNew); SendMessage(hwndStatic, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmPrev);
This sounds like a nop,since all you did was change the image,and then change it back.But the side effect is also that you made the staticcontrol go into your problem mode,and the original image will no longer be automaticallydestroyed.If you forget to destroy it yourself, then you have a leak.
Wait, it gets worse.
If you are using version 6 of the common controls,then things get even more confusing if youuse the
STM_SETIMAGE messageto change the
IMAGE_BITMAP of a
SS_BITMAP static control,and the bitmap you pass is a 32-bpp bitmap,and the image has a nonzero alpha channel,then the static control will make a copyof the bitmap you passed in and act as if you had passedthat copy instead.¹This by itself is no big deal, because the responsibilityfor destroying the image you passed in still resides withyou, the application,so the rules haven’t changed there.
The nasty bit is that the application also must assumeresponsibility for destroying the secret copy.That bitmap you didn’t even know existed and don’t havea handle to?Yeah, you’re on the hook for that one too.
Even more confusing is that if you send
STM_SETIMAGE a second time,it will replace the bitmap and return a handle tothe secret copy (which is a bitmap you’ve neverseen before).
This means that the following assertion can fire:
HBITMAP hbmPrev = SendMessage(hwndStatic, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmNew); HBITMAP hbmBack = SendMessage(hwndStatic, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmPrev); assert(hbmNew == hbmBack); // ??
You would think that the assertion is safe because all youdid was change the bitmap to
hbmNew, then changeit back.And when you change it back, the “previous value” is thevalue
hbmNew you set it to on the previous line.
Except that if
hbmNew satisfies the above magic criteria,then the value in
hbmBack is not
hbmNewbut rather the handle to the secret copy.
Which you have to remember to destroy.
The secret copy is not too secret.You can get a handle to it by sending the
STM_GETIMAGE message.Which you now need to do when you destroy the static control,just in case it’s the secret copy.You need to compare the current image against the one thatyou thought you passed in,and if they are different, then you have the secret copythat needs to be destroyed as an extra step.
Yes, this sucks.I apologize.
(My recommendation: To detect whether a “secret copy” occurred,do a
STM_GETIMAGE after your
STM_SETIMAGE and see if the handles match.)
¹ The secret copy is not an exact copy.(After all, if it were an exact copy, then there would be no needto create the copy.It could just use the handle you passed in.)Instead, the secret copy is a copy of the original,followed by someadditional mungingso that it can be displayedon the screen while respecting the alpha channel you passed in.