我知道基本区别,因为ReleaseComObject
只会将某些计数器减一,而FinalReleaseComObject
会将其减少为零。
所以我通常听到的是,调用FinalReleaseComObject
因为那样你就确定COM对象真的被释放了。
但是这让我想知道,这个反击有什么意义吗?如果你总是打电话给FinalReleaseComObject
,你不是在破坏这种机制吗?如果在您致电ReleaseComObject
之前该计数器不是一个,那么可能没有理由吗?
什么可能导致它不应该高于1?
提前致谢。
PS:我的COM体验只包括使用Excel Interop。不确定此问题是否属于该域的本地问题(即在Office Interop之外,不经常使用FinalReleaseComObject
)。
article Dan提到了关于在完成后使用ReleaseComObject
的讨论。据我所知article,这是正常的方法。我认为,如果你这样做,它应该工作正常。在对article的评论中,作者建议有人在循环中调用ReleaseComObject
直到它真正发布(article来自2006年,所以这类似于调用FinalReleaseComObject
})。但他也表示这可能是危险的。
如果你真的想让RCW在代码中的某个特定点调用Release(),你可以在循环中调用ReleaseComObject()直到返回值达到零。这应该确保RCW将调用Release()。但是,如果您这样做,请注意,当其他托管引用尝试使用该RCW时,它将导致异常。“
这让我相信,总是调用FinalReleaseComObject
确实不总是好主意,因为你可以在其他地方引起异常。正如我现在看到的那样,如果你完全确定可以的话,你应该只打电话。
尽管如此,我对此事的经验不多。我不知道怎么可以肯定。如果计数器不应该增加,那么解决这个问题不是更好吗?如果是这样,那么我会说FinalReleaseComObject
更像是一种黑客而不是最佳实践。
答案 0 :(得分:35)
一些序言......
运行时可调用包装器(RCW)仅在其包装的非托管COM接口上调用IUnknown.AddRef一次。但是,RCW还维护RCW本身的托管引用数量的单独计数。这是托管引用的单独计数,通过调用Marshal.ReleaseComObject来递减。当托管引用的计数达到零时,RCW在非托管COM接口上调用IUnknown.Release一次。
Marshal.FinalReleaseComObject通过一次调用将托管引用计数设置为零,从而立即调用包装的非托管IUnknown.Release方法(假设托管引用计数不是零)。
那么为什么要同时使用Marshal.ReleaseComObject和Marshal.FinalReleaseComObject?调用Marshal.FinalReleaseComObject只是避免编写一个循环,重复调用Marshal.ReleaseComObject,直到它返回0,当你希望表明你真的完成了使用COM对象现在。
为什么要使用Marshal.ReleaseComObject或Marshal.FinalReleaseComObject?我知道有两个原因:
第一个是确保由于对非托管IUnknown.Release()方法的调用而尽快释放被包装的COM对象使用的非托管资源(例如文件句柄,内存等) 。
第二个是确保调用非托管IUnknown.Release()方法的线程在你的控制之下,而不是终结器线程。
如果没有调用这些Marshal方法中的任何一个,RCW的终结器将最终在RCW被垃圾收集后的某个时间调用非托管的IUnknown.Release()方法。
有关确凿的详细信息,请参阅Visual C ++团队博客条目Mixing deterministic and non-deterministic cleanup