开发者

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

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 gotc开发者_高级运维has 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.


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.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜