.net - 在 .NET 中,如何为 OLE 分配的以 0 结尾的非托管 Unicode 字符串释放内存?
问题描述
通过使用IEnumSTATSTG::Next方法,我在STATSTG结构的pwcsName字段中获得了一个指向以 0 结尾的 Unicode 字符串的指针。
它的内存是由 OLE 分配的,但调用者有责任释放它。
我假设内存是使用非托管 COM 任务内存分配器分配的,但我在这里可能错了。如果假设是正确的,我会使用Marshal.FreeCoTaskMem(PtrToString)
释放该内存。
然而,论点是一个IntPtr
. 在 VB 中,我试图获得一个IntPtr
with
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
解决方案
如果你看一下STATSTG
你会读到的定义:
pwcsName
指向包含名称的以 NULL 结尾的 Unicode 字符串的指针。此字符串的空间由调用者调用和释放的方法分配(有关更多信息,请参阅 CoTaskMemFree)。
所以必须通过CoTaskMemFree()
(Marshal.CoTaskMemFree()
在.NET中)释放内存。
根据您将结构封送到 .NET 的方式,.NET 可能会自动释放它。我说“依赖”是因为您引用的STATSTG
是具有pwcsName
that 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 创建一个新String
的OLESTR
并释放OLESTR
. 我们很高兴。然后在下一次调用 时Next()
,在调用实际 API 之前,.NET Marshaller 会看到有一个漂亮的String
在您的结构中并将其编组为一个OLESTR
,认为它是您要传递给的数据Next()
。这显然是没有用的。现在的问题是:在为下一个结果创建新的之前,我们不知道 的实现是否Next()
会真正释放它OLESTR
(我们可以做一些测试,但我们不想这样做)。最简单(也是最可靠)的解决方案是在每次调用之前设置pwcsName
为Nothing
(在 C# 上) 。这样,.NET Marshaller 将看到一个漂亮的并将其编组为. 编组器完成的工作更少(编组字符串很昂贵),对我们来说更安全,不必考虑是否会被释放。null
.Next()
Nothing
null
OLESTR
后附录:我已经 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
,
推荐阅读
- pyspark - Pyspark pipelineModel.transform 错误“字段“cut_catVec”不存在。\n可用字段
- mysql-workbench - 如何使用win10将workbench 8.0主题更改为深色
- rust - Rust trait 由 supertrait 绑定,自身作为泛型参数
- java - 错误:android.view.WindowManager$BadTokenException:无法添加窗口——令牌 null 无效;您的活动正在运行吗?
- django - 错误子目录中的静态文件夹
- html - 导航栏未显示所有标题
- arrays - 如何在结构中附加切片
- assembly - 如何让代码在 Apple ii 上正确运行
- html - 如何在此标题栏中将标题居中?
- asp.net - 将 Docker 映像保存 IP 地址从 Windows 传输到 Linux