从非托管代码传递HBITMAP句柄到托管代码创建System.Drawing.Bitmap的安全性
Safety of passing HBITMAP handle from unmanaged to managed code for created a System.Drawing.Bitmap
我对托管/非托管互操作很陌生,所以我希望得到一些关于以下过程从非托管c++到托管c#获取位图的安全性的意见。基本思想是:
- c#调用一个互操作函数
FetchImage
,它在非托管c++中。它传递一个out int
参数。FetchImage
有对应的long *
参数。 - 在c++中,
FetchImage
在某个安全的地方创建了一个CBitmap
,即不是本地的,在它上面画一些东西,使用HandleToLong()
将位图的HBITMAP
句柄转换为long
,将其存储在c#的参数中,并返回。 - 在c#中,
out int
参数被转换为IntPtr
,并使用System.Drawing.Image.FromHbitmap
复制数据并产生System.Drawing.Bitmap
对象。然后c#调用另一个互操作函数ReleaseImage
。 - 在c++中,
ReleaseImage
释放与之前创建的CBitmap
相关的资源。
这是给没有耐心的人的要点。下面是更具体的代码示例。
c++互操作函数定义:
namespace {
std::unique_ptr< CBitmap > bitty;
}
HRESULT __stdcall Helper::FetchImage( /*[out]*/ long * hBitmap )
{
bitty.reset( new CBitmap );
// call CreateBitmap and then draw something,
// ensure it's not selected into a DC when done
*hBitmap = HandleToLong( bitty->GetSafeHandle() );
return S_OK;
}
HRESULT __stdcall Helper::ReleaseImage()
{
bitty.reset();
return S_OK;
}
互操作函数的IDL原型,在c#中包装在一个helper类中:
[id(1)] HRESULT FetchImage( long * hBitmap );
[id(2)] HRESULT ReleaseImage();
在helper类中生成这些c#原型:
void FetchImage( out int hBitmap );
void ReleaseImage();
调用它们的c#看起来像这样:
int ret;
helper.FetchImage( out ret );
Bitmap b = Image.FromHbitmap( (IntPtr)ret );
helper.ReleaseImage();
// do anything I want with b
我自己提出的唯一问题是从其他地方调用FetchImage
或ReleaseImage
使事情不同步的情况。所以我可能会有一个CBitmap
的列表,而不是只有一个,然后将句柄传递回ReleaseImage
,这样它只会从匹配的FetchImage
调用中销毁一个。
有我不知道的陷阱吗?我只是想确保我没有做一些危险的事情,因为我不知道有什么更好的
您可以直接声明释放HBITMAP是调用者的责任。这将简化您的c++代码,因为您可以删除ReleaseImage方法。例子:
HRESULT __stdcall Helper::FetchImage( /*[out]*/ HBITMAP * hBitmap )
{
*hBitmap = NULL; // assume failure
unique_ptr<CBitmap> bmp(new CBitmap);
// call CreateBitmap and then draw something,
// ensure it's not selected into a DC when done
*hBitmap = (HBITMAP)bmp->Detach();
return S_OK;
}
// Delete ReleaseImage and all supporting global variables...
// C# example:
IntPtr ret;
helper.FetchImage( out ret );
try {
Bitmap b = Image.FromHbitmap( ret );
} finally {
DeleteObject(ret); // pinvoke call into GDI
}
或者,您可以查看使用OleCreatePictureIndirect返回IPicture。这提供了一些优势:
- 调用者使用标准的COM引用计数释放返回的图像。这通常使调用者不必担心释放返回的图像(除非调用者是另一个需要手动调用IUnknown::Release的c++程序)。
- 与其他支持com的语言更好的兼容性,如VBA/VB6。IPicture是COM中传递图片的标准方式。
相关文章:
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- 使用std::multimap迭代器创建std::list
- 在全局变量中保存类的实例以重新创建类(创建"backup")
- 使用CMake创建QML插件
- 如何在c++中为模板函数实例创建快捷方式
- 在C++中,是否可以基于给定的标识符创建基类的新实例,反之亦然
- 创建一个函数以在输入为负数或零时输出字符串.第一次执行用户定义的函数
- OpenCV EqualizeHist()从彩色图像创建黑白图像
- 试图在visual studio上用C++创建一个桌面应用程序
- std::threads可以从Windows DLL中的全局变量创建/销毁吗?
- 如何在C++20中创建模板别名的推导指南
- 如何为模板化对象创建模板向量?VS正在投掷C3203
- 如何创建一个空的全局类并在启动时实例化它
- 无法创建抽象类的实例
- 链接到自行创建的dll失败
- 为什么我不能在不创建字符串变量的情况下使用函数的字符串输出
- 使用system()创建独立的子进程
- 需要使用System.diarostics在C 中创建秒表
- 从非托管代码传递HBITMAP句柄到托管代码创建System.Drawing.Bitmap的安全性
- 协同创建实例失败,并显示错误"System can not find the file specified"