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:
- C# calls an interop function,
FetchImage
, which is in the unmanaged C++. It passes anout int
param.FetchImage
has a correspondinglong *
param. - In C++,
FetchImage
creates aCBitmap
somewhere safe, ie not local, draws something on it, usesHandleToLong()
to convert the bitmap'sHBITMAP
handle to along
, stores it in the param for the C#, and returns. - Back in C#, the
out int
param is converted to anIntPtr
and usesSystem.Drawing.Image.FromHbitmap
to copy the data and produce aSystem.Drawing.Bitmap
object. - C# then calls another interop function,
ReleaseImage
. - In C++,
ReleaseImage
frees the resources associated with theCBitmap
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 CBitmap
s 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.
精彩评论