Safety of passing HBITMAP handle from unmanaged to managed code for created a System.Drawing.Bitmap

两盒软妹~` 提交于 2019-12-07 22:43:16

问题


I'm pretty new to managed/unmanaged interop, so I'm looking to get some opinions on how safe the following procedure is for getting a bitmap from unmanaged C++ to managed C#. The basic idea is:

  1. C# calls an interop function, FetchImage, which is in the unmanaged C++. It passes an out int param. FetchImage has a corresponding long * param.
  2. In C++, FetchImage creates a CBitmap somewhere safe, ie not local, draws something on it, uses HandleToLong() to convert the bitmap's HBITMAP handle to a long, stores it in the param for the C#, and returns.
  3. Back in C#, the out int param is converted to an IntPtr and uses System.Drawing.Image.FromHbitmap to copy the data and produce a System.Drawing.Bitmap object.
  4. C# then calls another interop function, ReleaseImage.
  5. In C++, ReleaseImage frees the resources associated with the CBitmap it created earlier.

That's the gist for the impatient. More specific code examples below.

C++ interop definitions for the functions:

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 prototypes for the interop functions, which are wrapped in a helper class in C#:

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

Produces these C# prototypes in the helper class:

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

And the C# that calls them looks kind of like this:

int ret;
helper.FetchImage( out ret );
Bitmap b = Image.FromHbitmap( (IntPtr)ret );
helper.ReleaseImage();
// do anything I want with b

The only issue I've come up with on my own is the case of a call to FetchImage or ReleaseImage from somewhere else getting things out of sync. So I'll probably have a list of CBitmaps instead of just one, then pass the handle back to ReleaseImage so it'll only destroy the one from the matching FetchImage call.

Are there any gotchas I'm not aware of? I do have this working, I just wanted to make sure I'm not doing something dangerous because I don't know any better.


回答1:


You could just declare that it is the caller's responsibility to free the HBITMAP. That would simplify your C++ code since you could remove the ReleaseImage method. Example:

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
}

Alternatively, you could look into returning an IPicture using OleCreatePictureIndirect. That provides some advantages:

  • Caller frees the returned image using standard COM reference counting. That generally frees the caller from worrying about freeing the returned image (unless the caller is another C++ program that needs to manually call IUnknown::Release).
  • Better compatibility with other COM-enabled languages, like VBA / VB6. IPicture is the standard way of passing around a picture in COM.


来源:https://stackoverflow.com/questions/7285237/safety-of-passing-hbitmap-handle-from-unmanaged-to-managed-code-for-created-a-sy

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!