2

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.

Ganesh Sittampalam
  • 28,821
  • 4
  • 79
  • 98
  • I am facing same problem . I tried your solution but not able to downcast to my object type. How did you downacast it? – Nishant Kumar Jun 09 '14 at 06:28
  • What goes wrong when you try? – Ganesh Sittampalam Jun 09 '14 at 06:29
  • **IDisposable^ value =(IDisposable^) (Marshal::GetObjectForIUnknown(IntPtr(test)));** Additional information: Unable to cast COM object of type 'System.__ComObject' to interface type 'System.IDisposable'. This operation failed because the QueryInterface call on the COM component for the interface with IID '' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)). – Nishant Kumar Jun 09 '14 at 06:57
  • That's probably because your object doesn't implement `IDisposable`, which was the interface I actually wanted to use. It may be that my approach only works on interfaces with the `ComVisible(true)` attribute - perhaps try defining such an interface yourself to contain the method you want to call? – Ganesh Sittampalam Jun 09 '14 at 07:45
  • You can also derive from `MarshalByRefObject` and then cast `GetObjectForIUnknown()` to the original managed type: http://stackoverflow.com/a/24121119/1768303 – noseratio Jun 11 '14 at 04:27

1 Answers1

0

I've now been using this approach in production for several months without any reported problems, so while it's not exactly definitive, I'm going to tentatively conclude that this approach is safe.

Ganesh Sittampalam
  • 28,821
  • 4
  • 79
  • 98