Reputation: 1741
I have a managed object that needs to be passed to an event handler in unmanaged code that takes an IntPtr. I'm not quite sure what I'm doing wrong, or if my approach is even correct. As I understand it, you need to pin the object to prevent GC from collecting it as follows:
MyCustomObject managedObject = new MyCustomObject();
GCHandle handle = GCHandle.Alloc(managedObject, GCHandleType.Pinned);
CustomEventHandler eventDelegate = new CustomEventHandler(PropertyEvent);
UnmanagedEventHandler(eventDelegate, handle.AddrOfPinnedObject());
private uint PropertyEvent(IntPtr inContext)
{
GCHandle handle = GCHandle.FromIntPtr(inContext); // throws exception
MyCustomObject managedObject = (MyCustomObject)handle.Target;
}
When I try to get a reference to my object inside the event, I get a FatalExecutionEngineError. Clearly I'm doing something wrong - and as a side note how does one prevent the "Object contains non-primitive or non-blittable data" error on complex objects? Do I have to marshal every type? Or is there an easier way to recover a reference to a complex managed object?
UPDATE:
Is this a possible fix? It seems to work but I don't know if there is anything wrong with this approach.
MyCustomObject managedObject = new MyCustomObject();
GCHandle handle = GCHandle.Alloc(managedObject, GCHandleType.Normal);
IntPtr ptr = GCHandle.ToIntPtr(handle);
CustomEventHandler eventDelegate = new CustomEventHandler(PropertyEvent);
UnmanagedEventHandler(eventDelegate, ptr);
private uint PropertyEvent(IntPtr inContext)
{
GCHandle handle = GCHandle.FromIntPtr(inContext); // throws exception
MyCustomObject managedObject = (MyCustomObject)handle.Target;
}
Upvotes: 2
Views: 971
Reputation: 942040
You are passing the wrong IntPtr, use GCHandle.ToIntPtr(handle) instead.
Also watch out for eventDelegate object, you need to prevent it from getting garbage collected. The GC cannot see that the unmanaged code is using it. Make sure it isn't a local variable, making it static is a simple way to avoid trouble.
Do note the unpleasant cost of keeping an object pinned for this long, it is a permanent rock in the road for the garbage collector. There's just no need, simply create your own "handle", just an integer that you increment for every call to UnmanagedEventHandler(). And store the object in a Dictionary<int, MyCustomObject>
so you can quickly get it back in the callback method. Also note that if there is in fact only one call to UnmanagedEventHandler (looks like it) then you don't need inContext at all.
Upvotes: 2