Marshal.FreeHGlobal()如何运作?

时间:2011-02-09 18:44:02

标签: c# .net c++ dll memory-management

我有一个基于C#的UI,它使用基于C ++的DLL。我的要求是将大量内存从C#传递给DLL。 DLL将写入此内存缓冲区并将其传递回C#。我使用过IntPtr&全局记忆功能来做到这一点。一切正常。

问题是,如何验证Marshal.FreeHGlobal()是否清理了内存?我正在使用大块内存,通常以MB为单位。所以我想确保立即清理内存。

2 个答案:

答案 0 :(得分:4)

如果您将有效句柄传递给Marshal.FreeHGlobal,则 将被释放。但由于该方法不返回值,因此无法确定是否已清除它。

如果您对是否将正确的内容传递给FreeHGlobal有疑问,那么我建议您的代码可能不像应该的那样干净。但是如果你真的想确定,那么你可以调用LocalFree Windows API函数,将它传递给你传递给FreeHGlobal的句柄:

[DllImport("kernel32", SetLastError=true)]
static extern IntPtr LocalFree(IntPtr mem);

// Now, to free a block of memory allocated with Marshal.AllocHGlobal
IntPtr rslt = LocalFree(memPtr);
if (rslt == IntPtr.Zero)
{
    // success!
}
else
{
    int err = Marshal.GetLastWin32Error();
    // do something with the error.
}

但是,我建议,如果你这样做,你调用LocalAlloc来分配内存而不是调用Marshal.AllocHGlobal,因为它可能(虽然可能不太可能).NET的未来版本可以使用LocalAlloc之外的其他内容来分配非托管内存。如果发生这种情况,那么依赖于LocalFree的代码就会中断。

答案 1 :(得分:0)

您也可以在托管内存中分配数组,然后创建GCHandle以允许从非托管内存访问它。

例如,我在这里打电话给PowerReadACValue; Win32 API函数接受LPBYTE缓冲区(等同于BYTE*)。

首先我用“空指针”(IntPtr.Zero)调用它,这样它就会告诉我需要什么大小的缓冲区(这会输出到bufferSize)。然后我分配缓冲区,并通过GCHandle的{​​{1}}传递指针。

AddrOfPinnedObject

或者,我可以使用uint type; uint bufferSize = 0; if (SafeNativeMethods.PowerReadACValue(IntPtr.Zero, this.m_planHandle, ref subGroupOfPowerSettingsGuid, ref powerSettingGuid, out type, IntPtr.Zero, ref bufferSize) != 0) { throw new Win32Exception(); } byte[] buffer = new byte[bufferSize]; GCHandle bufferPointer = default(GCHandle); try { bufferPointer = GCHandle.Alloc(buffer, GCHandleType.Pinned); if (SafeNativeMethods.PowerReadACValue(IntPtr.Zero, this.m_planHandle, ref subGroupOfPowerSettingsGuid, ref powerSettingGuid, out type, bufferPointer.AddrOfPinnedObject(), ref bufferSize) != 0) throw new Win32Exception(); } finally { if (bufferPointer.IsAllocated) bufferPointer.Free(); } 分配缓冲区,这也会返回Marshal.AllocHGlobal,然后用IntPtr释放它,但在这种情况下我需要使用来自托管代码的数组中的数据。如果我只是将数组指针传递给另一个非托管函数而不从托管代码触及它,这将是一个更好的方法(当GCHandle处于活动状态时,内存被锁定在内存中,影响垃圾收集器)。