我有一个假设的 COM 对象,具有以下签名
void MemAlloc(ref double[] test, int membercount)
在 C++ 中使用 new/malloc 分配内存。一旦在 C# 中使用 RCW,我如何确保正确释放内存?我认为 .NET 很难释放,考虑到在 C++ 中你需要知道它是否是用 new/malloc/mm_malloc 分配的,然后才能正确释放它。那么,清理 C++ 分配的数组的适当方法是什么?谢谢。
我相信您应该使用 CoTaskMemAlloc() 来存储您想要从托管端显式释放的内存。一旦内存不再可访问,CLR 将负责释放内存。如果您想显式释放它,您可以使用托管的 Marshal.CoTaskFree() 例程。
一般来说,互操作封送拆收器和 CLR 遵守释放内存的 COM 约定;接收者负责释放内存。因此,如果内存返回给托管调用者,CLR/Interop 封送拆收器通常会负责释放在本机调用中分配的内存。
来自 使用互操作封送拆收器进行内存管理 (msdn):
互操作封送拆收器始终尝试 释放非托管分配的内存 代码。此行为符合 COM 内存管理规则,但有所不同 来自管理本机 C++ 的规则。
如果您预期,可能会出现混乱 本机 C++ 行为(无内存 释放)使用平台调用时, 它会自动释放内存 指针。例如,调用 遵循 C++ 中的非托管方法 DLL 不会自动释放任何 记忆。
运行时总是使用 CoTaskMemFree 方法释放内存。 如果您正在使用的内存是 未使用 CoTaskMemAlloc 分配 方法,您必须使用 IntPtr 和 使用手动释放内存 适当的方法。
将其包装在实现 IDisposable 的对象中,并确保 C# 包装器已被释放。
这是我写的博客,介绍了实现 IDisposable 的简单方法。
这本书 .NET 2.0 Interoperbility Recipes 看起来很方便。看来很同意 Arnshea 所说的关于 CoTaskMemFree 的说法。
我几乎 100% 确定 CLR 不会自动释放为测试分配的内存(如果是 P/Invoke,我将 100% 确定)。原因是,CLR 首先如何知道您使用什么来分配内存?也就是说它没有。
更安全的写这个函数的方法如下
void MemAlloc(ref IntPtr arrayPtr, int membercount)
接到回电后,您可以执行以下操作。
var count = GetTheCount();
var arrayPtr = IntPtr.Zero;
obj.MemAlloc(ref arrayPtr, count);
byte[] test = MarshalThePtrToByteArray(arrayPtr, count);
Marshal.FreeCoTaskMem(arrayPtr);
这是 MarashalThePtrToByteArray 的快速但肮脏的实现
byte[] MarashalThePtrToByteArray(IntPtr ptr, int count) {
byte[] arr = new byte[count];
for ( int i = 0; i < count; i++ ) {
arr[i] = (byte)Marshal.PtrToStructure(ptr, typeof(byte));
ptr = new IntPtr(IntPtr.ToInt64() + Marshal.SizeOf(typeof(byte)));
}
return arr;
}