In .NET, there's a relatively well known problem with storing managed objects in unmanaged code as gcroot<ManagedObject>
to provide callbacks from the unmanaged code: the unmanaged code doesn't know what AppDomain to use when calling into managed code and sometimes it picks the wrong one, leading to "Cannot pass a GCHandle across AppDomains" errors.
The standard fix for the problem is to use a function pointer to a delegate, because the delegate can be used to "remember" the correct AppDomain: see http://lambert.geek.nz/2007/05/29/unmanaged-appdomain-callback/ for a full explanation.
However this solution is a little complicated and requires careful lifetime management of the delgate.
It seems like using the COM Callable Wrapper for the managed object works just as well: instead of storing a gcroot<ManagedObject>
, store the pointer as an IUnknown *
using GetIUnknownForObject
:
m_value =
static_cast<IUnknown*> (Marshal::GetIUnknownForObject(value).ToPointer());
Then we can do the reverse translation with GetObjectForIUnknown
before making the callback. The downside is losing a little bit of type safety because IUnknown *
loses the actual object type and you later have to downcast using something like
IDisposable^ value =
(IDisposable^) (Marshal::GetObjectForIUnknown(IntPtr(m_value)));
where m_value
is the IUnknown*
, but that seems like a small price to pay.
I've tried it and it seems to work ok in my use case, but are there any gotchas with taking this approach? It seems like it would apply anywhere one might use the delegate solution, so I wonder if I'm missing something about it.