首页 > 解决方案 > 在 .NET 中,如何为 OLE 分配的以 0 结尾的非托管 Unicode 字符串释放内存?

问题描述

通过使用IEnumSTATSTG::Next方法,我在STATSTG结构的pwcsName字段中获得了一个指向以 0 结尾的 Unicode 字符串的指针。

它的内存是由 OLE 分配的,但调用者有责任释放它。

假设内存是使用非托管 COM 任务内存分配器分配的,但我在这里可能错了。如果假设是正确的,我会使用Marshal.FreeCoTaskMem(PtrToString)释放该内存。

然而,论点是一个IntPtr. 在 VB 中,我试图获得一个IntPtrwith

PtrToString = CType(pwcsName, IntPtr)

语法被接受,但字符串类型错误 ( Input string is not in a correct format.)

因此我的问题:

我如何正确地IntPtr从结构记录的字段中获取一个?

或者更一般地说,我怎样才能成为好公民并防止潜在的内存泄漏?


这是相关代码:

    Dim oElements As IEnumSTATSTG = Nothing
    oStorage.EnumElements(0, IntPtr.Zero, 0, oElements)

    Dim oElement(0) As Microsoft.VisualStudio.OLE.Interop.STATSTG
    Dim uiFetched As UInt32 = 0

    oElements.Next(1, oElement, uiFetched)
    Do While uiFetched > 0
        'Work with oElement(0).pwcsName

        'Attempt to free the memory occupied by the name.
        Dim pName As IntPtr
        pName = CType(.pwcsName, IntPtr)
        Marshal.FreeCoTaskMem(pName)

        Yield ...
        oElements.Next(1, oElement, uiFetched)
    Loop

标签: .netmemory-managementmemory-leaksoleunmanaged-memory

解决方案


如果你看一下STATSTG你会读到的定义:

pwcsName
指向包含名称的以 NULL 结尾的 Unicode 字符串的指针。此字符串的空间由调用者调用和释放的方法分配(有关更多信息,请参阅 CoTaskMemFree)。

所以必须通过CoTaskMemFree()Marshal.CoTaskMemFree()在.NET中)释放内存。

根据您将结构封送到 .NET 的方式,.NET 可能会自动释放它。我说“依赖”是因为您引用的STATSTG是具有pwcsNamethat is a的版本string:在这种情况下,.NET 编组器将释放OLESTR. 如果您使用STATSTG使用 an 的结构,IntPtr则必须调用CoTaskMemFree.

要获得 .NET string,您可以使用Marshal.PtrToStringUni().

现在...在您的特定情况下,您处于极端情况。在方法的参数上,您可以指定它们是否是[In][Out]让 .NET Marshaller 知道它何时必须“工作”来编组数据(就在调用方法之前,或在调用方法之后,或两种方式)(但我在你的Next()定义中看不到它们),在结构的单个字段上我认为你不能(然后你肯定不能修改STATSTG)。所以这里发生的是,在调用.NET 之后Next(),在 C->.NET 的路上,.NET Marshaller 将从 .NET 创建一个新StringOLESTR并释放OLESTR. 我们很高兴。然后在下一次调用 时Next(),在调用实际 API 之前,.NET Marshaller 会看到有一个漂亮的String在您的结构中并将其编组为一个OLESTR,认为它是您要传递给的数据Next()。这显然是没有用的。现在的问题是:在为下一个结果创建新的之前,我们不知道 的实现是否Next()会真正释放它OLESTR(我们可以做一些测试,但我们不想这样做)。最简单(也是最可靠)的解决方案是在每次调用之前设置pwcsNameNothing(在 C# 上) 。这样,.NET Marshaller 将看到一个漂亮的并将其编组为. 编组器完成的工作更少(编组字符串很昂贵),对我们来说更安全,不必考虑是否会被释放。null.Next()NothingnullOLESTR

后附录:我已经 nugetted Microsoft.VisualStudio.OLE.Interop 16.7.30328.74并且我已经从 C# 程序中完成了 Go To Definition。我看到的方法定义不同(而且更详细):

int Next([In][ComAliasName("Microsoft.VisualStudio.OLE.Interop.ULONG")] uint celt, [Out][ComAliasName("Microsoft.VisualStudio.OLE.Interop.STATSTG")][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] STATSTG[] rgelt, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.ULONG")] out uint pceltFetched);

我看到STATSTG[] rgelt被标记为[Out],因此编组将仅在 C->.NET 方向上进行。您无需设置为Nothing,


推荐阅读