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

本文关键字:对象 引用 内存 保存 | 更新日期: 2023-09-27 18:10:21

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

问题:这是可能的和安全的,如果是,如何?

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

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

你必须担心这个对象需要固定多长时间。最多几秒钟是可以的,但如果长时间保持固定,它对GC是非常有害的。这是GC必须不断绕过的道路上的一块岩石。而且堆段永远不能回收,单个对象可能会花费几个兆字节。

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

无论哪种方式,对象必须是blittable,否则会得到运行时错误。这是一个昂贵的词,意思是对象必须具有简单的字段类型,即C程序能够正确读取的那种类型。不要使用bool。注意C程序中的声明,如果你写错了,很容易损坏堆。

当你控制'分发'和'接收后使用'阶段时,你可以简单地使用列表或数组并传递索引。

可以通过COM和CLR创建的代理(称为COM- callable Wrappers)来使用c#对象。

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

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

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

tlbexp.exe YourLibrary.dll

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