使用Image.FromHbitmap()构建位图时,原始位图句柄可以在何时删除?

5
http://msdn.microsoft.com/en-us/library/k061we7x%28VS.80%29.aspxImage.FromHbitmap() 文档中可以看到:

FromHbitmap 方法复制了 GDI 位图;所以您可以在创建新的 Image 后立即使用 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::Bitmap() 构造函数,该构造函数接受 HBITMAPHPALETTE 作为参数。
此版本的 Bitmap::Bitmap() 构造函数的文档在 http://msdn.microsoft.com/en-us/library/ms536314%28VS.85%29.aspx 中有这样的说明:

您负责删除 GDI 位图和 GDI 调色板。但是,在删除或超出范围之前,不应删除 GDI 位图或 GDI 调色板。

不要将当前(或先前)选择到设备上下文中的 GDI 位图或 GDI 调色板传递给 GDI+ Bitmap::Bitmap 构造函数。

此外,可以在 GdiPlusBitmap.h 的 C++ 部分的源代码中看到,该构造函数本身是 GdipCreateBitmapFromHBITMAP() 扁平 API 函数的包装器。
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+位图。
因此,总结如下:
  1. 原始位图句柄需要保留直到 .Net Bitmap 对象被处理吗?
  2. GDI+函数 GdipCreateBitmapFromHBITMAP() 是否复制源位图或只保留对原始位图的句柄?
  3. 为什么不应使用先前选择到设备上下文中的 HBITMAP?

同意这个冲突。本机Bitmap构造函数的文档一定是错误的。 - Hans Passant
1个回答

2

根据经验,.Net文档是正确的。您确实可以立即在传递给Image.FromHbitmap()的HBITMAP上调用DeleteObject(),而似乎没有任何负面影响。

根据我通过反向工程代码所学到的知识,同样适用于GDI+ Bitmap::Bitmap()构造函数和GDI+ GdipCreateBitmapFromHBITMAP()函数,尽管这与已发布的文档相矛盾。

也许GDI+文档过于保守,保留了将来版本保留提供的HBITMAP句柄的权利。如果GDI+发生了变化,.Net框架将不得不在将位图传递给GDI+之前复制位图,以保持其发布的合同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接