使用PInvoke中的结构后,是否需要释放内存

本文关键字:是否 释放 内存 PInvoke 结构 使用 | 更新日期: 2023-09-27 18:21:41

我需要做这样的事情吗:

TEXTMETRIC tm;
bool isTrueType = false;
if (NativeMethods.GetTextMetrics(hDC, out tm))
{
    isTrueType = ((PitchAndFamily)tm.tmPitchAndFamily & PitchAndFamily.TMPF_TRUETYPE) == PitchAndFamily.TMPF_TRUETYPE;
    IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<TEXTMETRIC>(tm));
    Marshal.StructureToPtr<TEXTMETRIC>(tm, ptr, true);
    Marshal.FreeHGlobal(ptr);
}

或者一旦函数退出,分配的内存会自动清理吗?我认为(从我读到的)是后者,但我不确定!

感谢任何澄清!

使用PInvoke中的结构后,是否需要释放内存

Marshal.StructureToPtr<TEXTMETRIC>(tm, ptr, true);

在这里使用true是非常错误的。使用AllocHGlobal()分配的内存未初始化,并且包含随机字节。它不包含需要在被方法覆盖之前发布的结构的早期版本。

从技术上讲,这可能会导致非常难以诊断的随机崩溃,具体取决于随机字节值。因为TEXTMETRIC不包含任何需要清理的成员,所以您侥幸逃脱了惩罚。FreeHGlobal()调用就足够了,不需要Marshal.DestroyStructure(),你应该把它放在finally块中,这样它就可以安全异常了。这回答了你的问题。

为了完成清理,只有当结构包含BSTR、SAFEARRAY或COM接口指针时才需要进行清理。具有显式发布调用并且在结构声明中需要[MarshalAs]属性的资源。当你使用pinvoke时,这种情况非常罕见。当您使用COM互操作时,也会在后台使用StructureToPtr(),这并不罕见,但CLR会自动进行调用。

您调用的函数GetTextMetrics希望调用方为结构分配和取消分配内存。如果使用AllocHGlobal进行分配,则必须使用FreeHGlobal解除分配。

然而,所有这些都是不必要的。当您声明tm时,您正在分配结构。没有什么需要了。通过从不调用AllocHGlobal来避免调用FreeHGlobal

TEXTMETRIC tm;
bool isTrueType = false;
if (NativeMethods.GetTextMetrics(hDC, out tm))
{
    isTrueType = ((PitchAndFamily)tm.tmPitchAndFamily & PitchAndFamily.TMPF_TRUETYPE) == PitchAndFamily.TMPF_TRUETYPE;
}

避免手动分配也可以避免对StructureToPtr进行其他答案所描述的中断呼叫,或者任何呼叫。

否,如果使用AllocHGlobal分配内存,则必须自己释放内存。否则,内存将被泄露。

这是文件中的相关部分

指向新分配的内存的指针。必须释放此内存使用Marshal.FreeHGlobal方法。

如果手动分配非托管内存(并且您正在这样做),则需要手动释放它。添加finally块以确保没有异常会干扰释放内存。

不过,您的示例没有多大意义,因为您将tm复制到了一个从未使用过的内存块中。