2

我有一个托管对象,它需要传递给采用 IntPtr 的非托管代码中的事件处理程序。我不太确定我做错了什么,或者我的方法是否正确。据我了解,您需要固定对象以防止 GC 收集它,如下所示:

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;
}

当我尝试在事件中获取对我的对象的引用时,我得到了 FatalExecutionEngineError。显然我做错了什么 - 作为旁注,如何防止复杂对象上的“对象包含非原始或非 blittable 数据”错误?我必须编组每种类型吗?或者有没有更简单的方法来恢复对复杂托管对象的引用?

更新

这是一个可能的修复吗?它似乎有效,但我不知道这种方法是否有任何问题。

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;
}
4

1 回答 1

2

您传递了错误的 IntPtr,请改用 GCHandle.ToIntPtr(handle)。

还要注意eventDelegate对象,您需要防止它被垃圾收集。GC 看不到非托管代码正在使用它。确保它不是局部变量,将其设为静态是避免麻烦的简单方法。

请注意将对象固定这么长时间的不愉快成本,对于垃圾收集器来说,它是一块永久的石头。没有必要,只需创建自己的“句柄”,只需为每次调用 UnmanagedEventHandler() 增加一个整数。并将对象存储在 a 中Dictionary<int, MyCustomObject>,以便您可以在回调方法中快速取回它。另请注意,如果实际上只有一次对 UnmanagedEventHandler 的调用(看起来像),那么您根本不需要inContext

于 2013-10-12T21:13:44.127 回答