5

http://msdn.microsoft.com/en-us/library/k061we7x%28VS.80%29.aspxImage.FromHbitmap()的文档:

FromHbitmap 方法制作 GDI 位图的副本;因此您可以在创建新图像后立即使用 GDIDeleteObject 方法释放传入的 GDI 位图。

这非常明确地表明,一旦创建 Bitmap 实例,就可以立即使用 DeleteObject 删除位图句柄。

然而,查看Image.FromHbitmap()with Reflector 的实现表明它是 GDI+ 函数的一个非常薄的包装器,GdipCreateBitmapFromHBITMAP().

关于 GDI+ 平面 API 函数的文档很少,但http://msdn.microsoft.com/en-us/library/ms533971%28VS.85%29.aspx说这GdipCreateBitmapFromHBITMAP()对应于Bitmap::Bitmap()采用 anHBITMAPHPALETTEas的构造函数参数。

此版本的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 的函数的包装器:

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?
4

1 回答 1

2

根据经验,.Net 文档似乎是正确的。确实可以立即调用DeleteObject()传递给的 HBITMAP,Image.FromHbitmap()并且这样做似乎没有任何不良影响。

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

也许 GDI+ 文档过于保守 - 保留在未来版本中保留提供的 HBITMAP 句柄的权利。如果 GDI+ 中发生这种变化,.Net 框架将不得不进行更改以通过在将位图传递给 GDI+ 之前制作位图副本来保留其发布的合同。

于 2010-06-21T17:39:18.220 回答