Reputation: 337
I am creatin a wrapper for an unmanaged third party library. This library allows to register callbacks. I got the callbacks working using delegates but the problem is that when registering a callback I could supply additional user data as void*
which will not be touched by the library but passed as parameter to the callback.
In code:
//interface
BOOL register_callback(LPCALLBACKFN pfnCallback, void* lpvUserData);
C++-Cli-side:
bool LibWrapper::RegisterCallback()
{
resetLastError();
CallbackDelegate^ callback = gcnew CallbackDelegate(
this,
&LibWrapper::test_callback);
GCHandle callbackHandle = GCHandle::Alloc(callback);
IntPtr ptrCallback = Marshal::GetFunctionPointerForDelegate(callback);
LPCALLBACKFN nativeCallbackPtr = \
static_cast<LPCALLBACKFN>(ptrCallback.ToPointer());
try
{
return register_callback(nativeCallbackPtr,/*void* data*/ nullptr);//!!!
}
catch(...)
{
setLastError("Unknown error when trying to register a callback.");
return false;
}
}
Is there any way I can pass an Object^
instead of just a nullptr
as user data?
The object itself could really be anything from a simple managed struct to a tree node obviously referencing other nodes, hence I'd like to find a way which does not involve creating a deep unmanaged copy of the whole object.
If it's not possible I might as well use a workaround and save my own list of requested callbacks and append the appropriate userData-Object myself. In that case it would never have to leave the managed world, but it still feels like cheating.
Upvotes: 0
Views: 923
Reputation: 942468
Well, you can, use GCHandle::Alloc() again, get the raw pointer with GCHandle::ToIntPtr() and recover the object reference again in the callback with GCHandle::FromIntPtr().
But this just isn't necessary. The power of a delegate over a function pointer is that it also captures your object reference. The this
argument in your CallbackDelegate constructor call. Which ensures that your test_callback() method can be an instance function and access the object members. Even when it is called from native code which knows beans about managed objects. It is called with a valid this reference by the stub that was created when you called Marshal::GetFunctionPointerForDelegate(). So just no need to use the void* argument, simply make the object a field of your class instead.
You ought to fret a bit about cleanup btw, be sure to call GCHandle::Free() when the native code can no longer make callbacks. Not doing so leaks the managed object forever.
Upvotes: 3