Marshal.FreeHGlobal() 如何工作?

发布于 2024-10-16 09:25:56 字数 203 浏览 2 评论 0原文

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

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

I have a C# based UI which uses C++ based DLL. My requirement was to pass a big chunk of memory from C# to DLL. DLL will write into this memory buffer and pass it back to C#. I have used IntPtr & global memory functions to do this. Everything works fine.

The question is, How to verify if the Marshal.FreeHGlobal() has cleaned the memory? I am using big chunk of memory, usually in terms of MBs. So I want to make sure that the memory is cleaned instantly.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

半衬遮猫 2024-10-23 09:25:56

如果您将有效句柄传递给 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 的代码将会崩溃。

If you pass a valid handle to Marshal.FreeHGlobal, it will be freed. But since the method doesn't return a value, you can't say for sure whether it was cleaned up.

If you have some doubt as to whether you're passing the right thing to FreeHGlobal, then I suggest that your code probably isn't as clean as it should be. But if you really want to make sure, then you could call the LocalFree Windows API function, passing it the handle that you would have passed to 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.
}

I would suggest, however, that if you do this, that you call LocalAlloc to allocate the memory rather than calling Marshal.AllocHGlobal, as it's possible (although probably unlikely) that future versions of .NET could use something other than LocalAlloc to allocate unmanaged memory. If that happened, then your code that depends on LocalFree would break.

偏爱你一生 2024-10-23 09:25:56

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

例如,这里我调用 <代码>PowerReadACValue; Win32 API 函数接受一个LPBYTE 缓冲区(相当于BYTE*)。

首先,我用“空指针”(IntPtr.Zero)调用它,这样它就会告诉我需要多大的缓冲区(这会输出到 bufferSize 中)。然后我分配缓冲区,并通过 GCHandle 的 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,然后使用 Marshal.FreeGlobal 释放它,但在这种情况下,我需要使用托管代码中的数组中的数据。如果我只是将数组指针传递给另一个非托管函数而不从托管代码中触及它,这将是一个更好的方法(当 GCHandle 处于活动状态时,内存被锁定在内存中,这会影响垃圾收集器)。

You can probably also allocate the array in managed memory, and then create a GCHandle to allow it to be accessed from unmanaged memory.

For example, here I call PowerReadACValue; the Win32 API function takes in an LPBYTE buffer (which equates to BYTE*).

First I call it with a "null pointer" (IntPtr.Zero), so that it will tell me what size buffer I need (this gets output into bufferSize). Then I allocate the buffer, and pass in the pointer via GCHandle's 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();
}

Alternatively, I could have allocated the buffer with Marshal.AllocHGlobal, which also returns me an IntPtr, and then afterwards free it with Marshal.FreeGlobal, but in this case I need to use the data in the array from managed code. If I were simply going to pass the array pointer to another unmanaged function without ever touching it from managed code, this would be a better way to go (while the GCHandle is active, the memory is locked into place in memory which affects the garbage collector).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文