在非托管内存中保存对托管对象的引用

Mar*_*ars -1 c c# pinvoke

我想将一个 C# 对象的引用放入非托管内存 (C),我猜是一个指针 (int),当 C 代码稍后回调到 C# 时,我想从非托管内存中取回引用,所以我可以解决它,并访问该对象。原因是 C 代码控制应该使用哪个对象,没有真正的替代方案。我对 C 代码的控制有限,不能选择 C++/CLI。

问题:这是否可能且安全,如果是,如何?

Han*_*ant 5

嗯,这是可能的。主要关注的是您的方案与垃圾收集器非常不兼容,它在压缩堆时移动内存中的对象。这是你可以停止的事情,你可以固定对象,这样 GC 就不能移动它。您使用 GCHandle.Alloc() 分配一个 GCHandleType.Pinned 句柄并将 GCHandle.AddrOfPinnedObject() 的返回值传递给您的 C 代码,大概是使用 pinvoke 调用。

您必须担心该对象需要固定多长时间。几秒钟,顶部,是可以的,但是如果您将其固定很长时间,它会对 GC 造成非常不利的影响。GC 必须不断地绕道而行。并且堆段永远无法回收,单个对象可能会花费您几兆字节。

在这种情况下,您应该考虑分配非托管内存并将对象复制到其中。使用 Marshal.AllocHGlobal() 分配,使用 Marshal.StructureToPtr() 将对象复制到其中。如果您修改对象并且更改也需要对 C 代码可见,则可能多次。

无论哪种方式,对象都必须是blittable,否则会出现运行时错误。一个昂贵的词只是意味着对象必须具有简单的字段类型,C 程序可以正确读取这种类型。不要使用bool。小心 C 程序中的声明,当你出错时很容易破坏堆。