从OLE32获得存储对象后,我需要清理非托管代码吗?



我从OLE32的StgCreateStorageEx函数中获得一个IStorage对象。它的声明如下:

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

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

文档提醒我们:

应用程序在使用完存储指针后必须释放它的存储指针要释放已使用内存的存储对象。

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

我碰巧看到这份文件元帅。ReleaseComObject被Visual Studio开发者认为是危险的。只…

我不确定我是否完全理解了原因。然而,这似乎是一个明确的建议完全远离ReleaseComObject但是…

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

如何防止内存泄漏?

也许这应该放在注释中…

答案是框架为你做Release()。但是,它是以一种不确定的方式进行的。当制定了清理标准后,它将在垃圾收集周期中执行此操作。

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

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

YMMV

最新更新