来自 Image.FromHbitmap() 文档“nofollow noreferrer”">http://msdn.microsoft.com/en-us/library/k061we7x%28VS.80%29.aspx:
FromHbitmap 方法复制 GDI 位图;因此您可以在创建新图像后立即使用 GDIDeleteObject 方法释放传入的 GDI 位图。
这非常明确地表明,一旦创建了 Bitmap 实例,就可以使用 DeleteObject 立即删除位图句柄。
然而,通过 Reflector 查看 Image.FromHbitmap()
的实现,会发现它是 GDI+ 函数 GdipCreateBitmapFromHBITMAP()
的一个非常薄的包装。
关于 GDI+ 平面 API 函数的文档非常少,但是 http://msdn.microsoft.com/en-us/library/ms533971%28VS.85%29.aspx 表示 GdipCreateBitmapFromHBITMAP()
对应于 位图: :Bitmap()
构造函数,采用 HBITMAP
和 HPALETTE
作为参数。
此版本的 Bitmap::Bitmap()
构造函数的文档位于 http://msdn.microsoft.com/en-us/library/ms536314%28VS.85%29.aspx 有这样的说法:
您负责删除 GDI 位图和 GDI 调色板。但是,在 GDI+ Bitmap::Bitmap 对象被删除或超出范围之前,不应删除 GDI 位图或 GDI 调色板。
不要将当前(或之前)选择到设备上下文中的 GDI 位图或 GDI 调色板传递给 GDI+ Bitmap::Bitmap 构造函数。
此外,我们可以在 GdiPlusBitmap.h 中 GDI+ 的 C++ 部分的源代码中看到,所涉及的 Bitmap::Bitmap()
构造函数本身就是 GdipCreateBitmapFromHBITMAP()<平面 API 中的 /code> 函数:
inline
Bitmap::Bitmap(
IN HBITMAP hbm,
IN HPALETTE hpal
)
{
GpBitmap *bitmap = NULL;
lastResult = DllExports::GdipCreateBitmapFromHBITMAP(hbm, hpal, &bitmap);
SetNativeImage(bitmap);
}
我不容易看到的是该功能的核心 GdipCreateBitmapFromHBITMAP()
的实现,但文档中的两个注释似乎是矛盾的。 .Net 文档说我可以立即删除位图句柄,GDI+ 文档说必须保留位图句柄直到删除包装对象,但两者都基于相同的 GDI+ 函数。
此外,GDI+ 文档警告不要使用当前或之前选择到设备上下文中的源 HBITMAP。虽然我可以理解为什么当前不应将位图选择到设备上下文中,但我不明白为什么会出现警告,禁止使用先前选择到设备上下文中的位图。这似乎会阻止使用使用标准 GDI 在内存中创建的 GDI+ 位图。
因此,总而言之:
- 在 .Net Bitmap 对象被释放之前,是否需要保留原始位图句柄?
- GDI+ 函数
GdipCreateBitmapFromHBITMAP()
是复制源位图还是仅仅保留原始位图的句柄?
- 为什么我不应该使用之前选择到设备上下文中的 HBITMAP?
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:
- Does the original bitmap handle need to be kept around until the .Net Bitmap object is disposed?
- Does the GDI+ function,
GdipCreateBitmapFromHBITMAP()
, make a copy of the source bitmap or merely hold onto the handle to the original?
- Why should I not use an HBITMAP that was previously selected into a device context?
发布评论
评论(1)
根据经验,.Net 文档似乎是正确的。人们确实可以立即对传递给
Image.FromHbitmap()
的 HBITMAP 调用DeleteObject()
,而且这样做似乎不会产生任何不良影响。根据我通过对代码进行逆向工程所学到的知识,同样的情况也适用于 GDI+
Bitmap::Bitmap()
构造函数和 GDI+GdipCreateBitmapFromHBITMAP()
函数,尽管这与已发布的文件相矛盾。也许 GDI+ 文档过于保守 - 保留在未来版本中保留所提供的 HBITMAP 句柄的权利。如果 GDI+ 中发生这种变化,.Net 框架必须进行更改,以在将位图传递给 GDI+ 之前制作位图的副本,以保留其已发布的约定。
Empirically, it seems the .Net documentation is correct. One can indeed immediately call
DeleteObject()
on the HBITMAP passed toImage.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+.