如何解决在 .NET 中,如何为由 OLE 分配的以 0 结尾的非托管 Unicode 字符串释放内存?
我使用 pwcsName 方法获得了指向 STATSTG 结构的 IEnumSTATSTG::Next 字段中以 0 结尾的 Unicode 字符串的指针。
它的内存是由 OLE 分配的,但调用者有责任释放它。
我假设内存是使用非托管 COM 任务内存分配器分配的,但我在这里可能是错误的。如果假设正确,我会使用 Marshal.FreeCoTaskMem(PtrToString)
来释放该内存。
然而,参数是一个 IntPtr
。在VB中,我试图用
IntPtr
PtrToString = CType(pwcsName,IntPtr)
接受语法,但字符串类型错误 (Input string is not in a correct format.
)
因此我的问题:
如何从结构记录的字段中正确获取 IntPtr
?
或者更一般地说,我怎样才能成为好公民并防止潜在的内存泄漏?
这是相关代码:
Dim oElements As IEnumSTATSTG = nothing
oStorage.EnumElements(0,IntPtr.Zero,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,uiFetched)
Loop
解决方法
如果你看一下 STATSTG
的定义,你会读到:
pwcsName
指向包含名称的以 NULL 结尾的 Unicode 字符串的指针。此字符串的空间由调用者调用和释放的方法分配(有关详细信息,请参阅 CoTaskMemFree)。
因此内存必须由 CoTaskMemFree()
(.NET 中的 Marshal.CoTaskMemFree()
)释放。
根据您如何将结构编组到 .NET,.NET 可能会自动释放它。我说“依赖”是因为您引用的 STATSTG
版本具有 pwcsName
是 string
:在这种情况下,.NET 编组器将释放 OLESTR
。如果您使用使用 STATSTG
的 IntPtr
结构,则必须调用 CoTaskMemFree
。
要获得 .NET string
,您可以使用 Marshal.PtrToStringUni()
。
现在...在您的特定情况下,您处于极端情况。在方法的参数上,您可以指定它们是 [In]
或 [Out]
以让 .NET Marshaller 知道它何时必须“工作”来编组数据(就在调用方法之前,或调用之后方法,或两种方式)(但我在你的 Next()
定义中没有看到它们),在结构的单个字段上我认为你不能(然后你肯定不能修改 {{1} })。所以这里发生的是,在调用 STATSTG
之后,在 C->.NET 的过程中,.NET Marshaller 将从 Next()
创建一个新的 String
并释放 {{1 }}。我们很高兴。然后在对 OLESTR
的 next 调用中,在调用实际 API 之前,.NET Marshaller 将看到您的结构中有一个漂亮的 OLESTR
并将其编组为 { {1}},认为这是您要传递给 Next()
的数据。这显然是没有用的。现在的问题是:在为下一个结果创建一个新的之前,我们不知道 String
的实现是否真的会释放这个 OLESTR
(我们可以做一些测试,但我们不想到)。最简单(也是最可靠)的解决方案是在每次调用 Next()
之前将 Next()
设置为 OLESTR
(在 C# 上为pwcsName
)。通过这种方式,.NET Marshaller 将看到一个漂亮的 Nothing
并将其编组到 null
。编组器完成的工作更少(编组字符串很昂贵),对我们来说更安全,不必考虑 .Next()
是否会被释放。
帖子附录:我已经开发了 Microsoft.VisualStudio.OLE.Interop 16.7.30328.74 并且我已经从 C# 程序中完成了 Go To Definition。我看到的方法定义不同(而且更详细):
Nothing
我看到 null
被标记为 OLESTR
,因此只会在 C->.NET 方向上进行编组。您不需要设置为 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);
,
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。