何时需要调用ReleaseComObject?

9
在Microsoft Office AddIn中,我们在事件中传递COM对象。以一个具体的案例为例,当Word打开文档时,我们会被调用并传递一个Document对象。那么什么时候需要调用Marshal.ReleaseComObject()呢?
  1. 如果我们访问了Document对象,我们需要对它进行释放吗?或者我们可以假设Word已经访问了它并将清理它?
  2. 如果我们访问Document.Name,它会给我们一个字符串。由于字符串不是COM对象,因此我们不需要清理它 - 对吗?
  3. 但是,如果我们访问任何返回包装COM对象的类的成员(也就是由成员方法/函数返回的任何类),我们需要对其进行释放 - 对吗?
如果我们错过了一次释放会发生什么?我们持有的任何COM对象都会在我们自己的类中进行包装,并实现IDisposable。当我们完成操作后,我们调用Dispose()。但是,处理这个问题的代码有些复杂,我猜测我们偶尔会遇到我们没有调用Dispose的情况。
我们最好拥有一个终结器,然后为每个对象的实例增加开销吗(很多!)?还是我们最好拥有一小部分从不被释放的Word COM对象呢?
谢谢 - dave

2
已经有数百个关于此的问题,可以通过方法名称轻松找到。同样的主题一遍又一遍,RCW是托管对象,因此遵守垃圾回收生命周期规则。您可以使用GC.Collect强制进行垃圾回收。 - Hans Passant
2
我在S/O上阅读了12个相关条目。在阅读完所有内容后,我留下了以上这些问题。MS Office COM对象会更加复杂,因为你不确定Office到底在做什么。 - David Thielen
1
只有一种COM,Office不使用特殊版本。对象模型越复杂,你释放ReleaseComObject的可能性就越小。这就是为什么你把它留给垃圾回收器,它从不错过任何一个“猴子”的原因。 - Hans Passant
3
黑魔法在软件工程中没有立足之地。在C# 4版本允许的语法扩展中,有更多的方法可以避免错过重要细节。Range.Cell[x, y].Value语法是一个出色的方法,它隐藏了三只无法被摆脱的猴子,因为你没有将接口引用分配给任何地方,所以需要让垃圾收集器处理。 - Hans Passant
2
是的,这是一个非常流行的错觉。但这并不意味着它就是正确的错觉。只有Sahuagin做对了。这一点一再出现,因为每个人都很难避免犯错。这和写一个不会泄漏内存的C程序一样困难。显然我无法说服你,但这并不重要。通过发布一个展示问题的片段来使这成为一个真正的问题。 - Hans Passant
显示剩余4条评论
1个回答

3
简而言之,每次引用COM对象都必须释放。如果不释放,进程将一直停留在内存中。
这个规则不适用于值类型(如字符串等)和您没有明确引用的子COM对象。
唯一的例外可能是作为事件参数传递给您的COM对象。我认为您不需要释放它们,但我不确定。然而,快速测试应该可以确认这一点。(尝试使用并且不释放COM对象的插件。看看插件是否开始表现出异常行为或者关闭应用程序后是否有任何相关的进程仍在运行。)
针对您的具体问题:
1. 如果我们访问Document对象,我们是否需要调用release?还是可以假设Word已经访问了它并会清理它?-- 您必须释放它。
2. 如果我们访问Document.Name,那么会得到一个字符串。由于字符串不是COM对象,因此我们不需要清理它-正确吗?-- 您不需要清理值类型。
3. 但是,如果我们访问返回包装COM对象的类的任何成员(这是由成员方法/函数返回的任何类),我们需要在其上调用release-正确吗?-- 如果您没有明确引用它,则不需要释放它。(有一些例外情况。例如,在我的这个问题中,我发现一个特定的COM方法正在实例化一个COM对象,并且从未释放它。由于我无法控制方法的实现,我的唯一选择是避免使用该方法。)
4. 如果我们错过了一个release会发生什么?-- 进程(winword.exe、excel.exe等)将保留在内存中。随着时间的推移,所有这些未终止的进程将耗尽机器上所有可用的内存。
5. 我们最好有一个finalizer,然后为每个对象实例增加开销(很多!)还是最好拥有一小部分Word COM对象从未被释放?-- 您最好使用finalizer。始终释放您的COM对象!我发现这里概述的步骤最有效。(这些步骤使用FinalReleaseComObject,这比ReleaseComObject更受欢迎。)我还建议不要杀死Office进程作为不释放COM的替代方案。随着时间的推移,这将导致Office互操作出现问题(我从未完全理解过)。

首先,感谢您!其次,在第一个问题上,当我访问 Document 对象时,它是否会变成马绍尔对象,这就是我需要释放它的原因?释放传递给我的对象似乎不太对。这并不意味着它是错误的,但是感觉不太对。 - David Thielen
抱歉,我错过了文档对象是通过事件参数传递给您的事实。我更新了我的问题来解决这个问题。您使用什么来创建您的插件?您没有使用VSTO吗? - Keith
我们在我们的Add-In中使用旧的IExtensibility API。当我们开始使用VSTO时,它非常有限(现在不清楚)。在尝试发布并查看其是否出现问题之前,请三思而后行 - 这往往会导致未来的问题。 - David Thielen
经过更多的研究,我几乎可以确定您不需要释放作为事件参数传递给您的COM对象。这就是VSTO项目的工作方式。 - Keith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接