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

Save reference to managed object in unmanaged memory

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

问题:这是否可能且安全,如果可以,怎么做?

当您控制 'handing out' 和 'use after receiving back' 阶段时,您可以简单地使用列表或数组并传递索引。

可以通过名为 COM-Callable Wrappers 的 CLR 创建的 COM 和代理使用 C# 对象。

您只需分配一个 GUID 程序集属性来标识 COM 类型库,例如:

[assembly: Guid ("39ec755f-022e-497a-9ac8-70ba92cfdb7c")]

然后使用Type Library Exporter工具(tlbexp.exe)生成COM类型库(.tlb)文件,可以在COM中使用世界:

tlbexp.exe YourLibrary.dll

如果你指的是 C# 意义上的安全,那么肯定是不安全的,因为你将在非托管世界中使用对象,并且生命周期是通过引用计数从 COM 端控制的,而不是 CLR 的 GC .

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

您必须担心该对象需要固定多长时间。几秒钟,最重要的是,没关系,但如果长时间固定它,它会对 GC 造成相当大的损害。这是 GC 必须不断绕过的道路上的一块石头。而且堆段永远无法回收,单个对象可能会花费您几兆字节。

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

无论哪种方式,对象必须 blittable 否则会出现运行时错误。一个昂贵的词,仅表示对象必须具有简单的字段类型,即 C 程序有机会正确读取的类型。不要使用 bool。小心C程序中的声明,当你写错的时候很容易破坏堆。