将HBITMAP句柄从非托管代码传递到托管代码以便创建System.Drawing.Bitmap的安全性

时间:2011-09-02 14:51:02

标签: c# c++ interop marshalling

我是托管/非托管互操作的新手,所以我希望得到一些意见,了解以下过程对于从非托管C ++到托管C#获取位图的安全性。基本思路是:

  1. C#调用一个互操作函数FetchImage,它位于非托管C ++中。它传递out int参数。 FetchImage有相应的long *参数。
  2. 在C ++中,FetchImage创建一个安全的CBitmap,即不是本地的,在其上绘制内容,使用HandleToLong()将位图的HBITMAP句柄转换为{{ 1}},将其存储在C#的参数中,然后返回。
  3. 返回C#,long参数转换为out int并使用IntPtr复制数据并生成System.Drawing.Image.FromHbitmap个对象。
  4. C#然后调用另一个互操作函数System.Drawing.Bitmap
  5. 在C ++中,ReleaseImage释放与之前创建的ReleaseImage相关联的资源。
  6. 这是不耐烦的要点。下面是更具体的代码示例。

    函数的C ++互操作定义:

    CBitmap

    interop函数的IDL原型,它们包装在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;
    }
    

    在帮助程序类中生成这些C#原型:

    [id(1)] HRESULT FetchImage( long * hBitmap );
    [id(2)] HRESULT ReleaseImage();
    

    调用它们的C#看起来像这样:

    void FetchImage( out int hBitmap );
    void ReleaseImage();
    

    我自己提出的唯一问题是来自其他地方的int ret; helper.FetchImage( out ret ); Bitmap b = Image.FromHbitmap( (IntPtr)ret ); helper.ReleaseImage(); // do anything I want with b FetchImage来电,导致事情不同步。所以我可能会有一个ReleaseImage的列表而不是一个,然后将句柄传递回CBitmap,这样它只会破坏匹配的ReleaseImage调用中的一个。{ / p>

    有没有我不知道的陷阱?我确实有这个工作,我只是想确保我没有做一些危险的事情,因为我不知道更好。

1 个答案:

答案 0 :(得分:1)

您可以声明释放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
}

或者,您可以尝试使用IPicture返回OleCreatePictureIndirect。这提供了一些优势:

  • 调用者使用标准COM引用计数释放返回的图像。这通常使调用者不必担心释放返回的图像(除非调用者是另一个需要手动调用IUnknown :: Release的C ++程序)。
  • 与其他支持COM的语言(如VBA / VB6)更好地兼容。 IPicture是在COM中传递图片的标准方式。