为什么使用FinalReleaseComObject而不是ReleaseComObject?

时间:2009-12-01 15:50:16

标签: .net com interop

我知道基本区别,因为ReleaseComObject只会将某些计数器减一,而FinalReleaseComObject会将其减少为零。

所以我通常听到的是,调用FinalReleaseComObject因为那样你就确定COM对象真的被释放了。

但是这让我想知道,这个反击有什么意义吗?如果你总是打电话给FinalReleaseComObject,你不是在破坏这种机制吗?如果在您致电ReleaseComObject之前该计数器不是一个,那么可能没有理由吗?

什么可能导致它不应该高于1?

提前致谢。

PS:我的COM体验只包括使用Excel Interop。不确定此问题是否属于该域的本地问题(即在Office Interop之外,不经常使用FinalReleaseComObject)。

更新1

article Dan提到了关于在完成后使用ReleaseComObject的讨论。据我所知article,这是正常的方法。我认为,如果你这样做,它应该工作正常。在对article的评论中,作者建议有人在循环中调用ReleaseComObject直到它真正发布(article来自2006年,所以这类似于调用FinalReleaseComObject })。但他也表示这可能是危险的。

  

如果你真的想让RCW在代码中的某个特定点调用Release(),你可以在循环中调用ReleaseComObject()直到返回值达到零。这应该确保RCW将调用Release()。但是,如果您这样做,请注意,当其他托管引用尝试使用该RCW时,它将导致异常。“

这让我相信,总是调用FinalReleaseComObject确实总是好主意,因为你可以在其他地方引起异常。正如我现在看到的那样,如果你完全确定可以的话,你应该只打电话。

尽管如此,我对此事的经验不多。我不知道怎么可以肯定。如果计数器不应该增加,那么解决这个问题不是更好吗?如果是这样,那么我会说FinalReleaseComObject更像是一种黑客而不是最佳实践。

1 个答案:

答案 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