Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When constructing a Bitmap with Image.FromHbitmap(), how soon can the original bitmap handle be deleted?

From the documentation of Image.FromHbitmap() at http://msdn.microsoft.com/en-us/library/k061we7x%28VS.80%29.aspx :

The FromHbitmap method makes a copy of the GDI bitmap; so you can release the incoming GDI bitmap using the GDIDeleteObject method immediately after creating the new Image.

This pretty explicitly states that the bitmap handle can be immediately deleted with DeleteObject as soon as the Bitmap instance is created.

Looking at the implementation of Image.FromHbitmap() with Reflector, however, shows that it is a pretty thin wrapper around the GDI+ function, GdipCreateBitmapFromHBITMAP().

There is pretty scant documentation on the GDI+ flat API functions, but http://msdn.microsoft.com/en-us/library/ms533971%28VS.85%29.aspx says that GdipCreateBitmapFromHBITMAP() corresponds to the Bitmap::Bitmap() constructor that takes an HBITMAP and an HPALETTE as parameters.

The documentation for this version of the Bitmap::Bitmap() constructor at http://msdn.microsoft.com/en-us/library/ms536314%28VS.85%29.aspx has this to say:

You are responsible for deleting the GDI bitmap and the GDI palette. However, you should not delete the GDI bitmap or the GDI palette until after the GDI+ Bitmap::Bitmap object is deleted or goes out of scope.

Do not pass to the GDI+ Bitmap::Bitmap constructor a GDI bitmap or a GDI palette that is currently (or was previously) selected into a device context.

Furthermore, one can see in the source code for the C++ portion of GDI+ in GdiPlusBitmap.h that the Bitmap::Bitmap() constructor in question is itself a wrapper for the GdipCreateBitmapFromHBITMAP() function from the flat API:

inline 
Bitmap::Bitmap(
    IN HBITMAP hbm, 
    IN HPALETTE hpal
    )
{
    GpBitmap *bitmap = NULL;

    lastResult = DllExports::GdipCreateBitmapFromHBITMAP(hbm, hpal, &bitmap);

    SetNativeImage(bitmap);
}

What I can't easily see is the implementation of GdipCreateBitmapFromHBITMAP() that is the core of this functionality, but the two remarks in the documentation seem to be contradictory. The .Net documentation says I can delete the bitmap handle immediately, and the GDI+ documentation says the bitmap handle must be kept until the wrapping object is deleted, but both are based on the same GDI+ function.

Furthermore, the GDI+ documentation warns against using a source HBITMAP that is currently or previously selected into a device context. While I can understand why the bitmap should not be selected into a device context currently, I do not understand why there is a warning against using a bitmap that was previously selected into a device context. That would seem to prevent use of GDI+ bitmaps that had been created in memory using standard GDI.

So, in summary:

  1. Does the original bitmap handle need to be kept around until the .Net Bitmap object is disposed?
  2. Does the GDI+ function, GdipCreateBitmapFromHBITMAP(), make a copy of the source bitmap or merely hold onto the handle to the original?
  3. Why should I not use an HBITMAP that was previously selected into a device context?
like image 390
GBegen Avatar asked Nov 06 '22 12:11

GBegen


1 Answers

Empirically, it seems the .Net documentation is correct. One can indeed immediately call DeleteObject() on the HBITMAP passed to Image.FromHbitmap() and there appear to be no ill effects from having done so.

Based on what I have learned by reverse engineering the code, the same would apply to the GDI+ Bitmap::Bitmap() constructor and the GDI+ GdipCreateBitmapFromHBITMAP() function as well, although this contradicts the published documentation.

Perhaps the GDI+ documentation is being overly conservative - reserving the right to, in a future version, hold on to the provided HBITMAP handle. Should that change ever occur in GDI+, the .Net framework would have to change to preserve their published contract by making a copy of the bitmap before passing it to GDI+.

like image 109
GBegen Avatar answered Nov 15 '22 07:11

GBegen