从 OLE32 获取 IStorage 对象后是否需要清理非托管代码?

问题描述

我从 OLE32 的 IStorage 函数获得了一个 StgCreateStorageEx 对象。这是它的声明:

Imports Microsoft.VisualStudio.OLE.Interop

<System.Runtime.InteropServices.DllImport("ole32.dll",SetLastError:=True,CharSet:=CharSet.Unicode)>
Private Shared Function StgCreateStorageEx(
    <MarshalAs(UnmanagedType.LPWStr)>
        ByVal pwcsName As String,ByVal grfMode As UInt32,ByVal stgFmt As UInt32,ByVal grfAttrs As UInt32,ByRef pStgOptions As sstgOptions,ByVal pSecurityDescriptor As IntPtr,<[In]()> ByRef riid As Guid,<[Out]()> ByRef ppObjectOpen As IStorage) As Int32
End Function

调用者设置标志和结构成员,然后在 IStorage 对象中获取新的 oStorage As IStorage 对象。到目前为止一切都很好。

documentation 提醒我们:

应用程序在完成后必须释放其 IStorage 指针 用于释放内存的存储对象。

由于对象是由 COM 创建并消耗内存,而且我们都不喜欢内存泄漏,所以标准的做法是正确实现 Idisposable 并处理调用者的 oStorage对象,通过 Marshal.ReleaseComObject(oStorage) 方法但是...

我碰巧遇到了 Visual Studio 开发人员编写的文档 Marshal.ReleaseComObject Considered Dangerous仅此而已...

我不确定我是否彻底理解了原因。但是,完全远离ReleaseComObject似乎是一个明确的建议。 但是...

文件源于 2010 年。十多年后,这仍然是正确的建议吗? 还有...

否则我将如何防止内存泄漏?

解决方法

也许这应该在评论中...

答案是该框架确实为您执行了 Release()。但是,它以非确定性的方式进行。当清理标准制定后,它会在垃圾收集周期中进行。

如果您在块中使用代码,并且您确定不会传递任何其他对要保存以供以后使用的存储的引用,则基于多年经验,我个人的建议是调用 Marshal.ReleaseComObject()与 oStorage 对象。当然,如果您打开任何流或其他子存储,请确保先关闭并释放这些对象。如果您打开的任何流或子存储被保存以备后用,则不要释放主存储。

如果你打开一个存储然后什么都不做——你最终让框架调用它——如果你再次尝试打开存储并且框架没有销毁前一个对象,那么调用可能是再次打开存储的功能将失败。它可能会因为权限问题而失败,因为它已经打开了……打开,因为它还没有在 GC 周期中被框架释放。

天啊