我可以对ReleaseComObject有所了解吗?我可以忽略它吗?

时间:2014-06-20 15:08:21

标签: c# .net com vsto com-interop

我一直在VBA开发办公室解决方案已有一段时间了,并且对VBA中的办公室开发有相当完整的了解。我已经决定是时候用.Net学习一些真正的编程了,并且遇到了一些问题。

浏览了大量文章和论坛(此处和其他地方),在使用COM对象时,似乎有一些关于.Net内存管理的混合信息。

有些人说我应该总是确定性地释放COM对象,而其他人说我几乎不应该这样做。

人们说我应该这样做:

第861页的

The book 'Professional Excel Development'

This stack exchange question已经回答说“你必须释放你对COM对象的每一个引用。如果你没有,那么这个进程将留在内存中”

This blog建议用它来解决他的问题。

人们说我不应该这样做:

This MSDN blog by Eric Carter声明“在VSTO场景中,您通常不必使用ReleaseCOMObject。”

由Eric Carter共同撰写的

The book 'VSTO for Office 2007'似乎没有提及任何内存管理或ReleaseComObject。

This MSDN blog by Paul Harrington说不要这样做。

提出混合建议的人:

Jake Ginnivan说我应该总是在没有离开方法范围的COM对象上进行。如果COM对象离开方法范围,则忘记它。为什么我不能一直忘记它呢?

Paul Harrington的博客似乎暗示MS的建议在过去的某个时候发生了变化。调用ReleaseCOMObject曾经是最佳实践,但现在不是了吗?我可以将更精细的内存管理细节留给MS,并假设一切都很好吗?

2 个答案:

答案 0 :(得分:1)

我尝试在关于ReleaseComObject的互操作开发中坚持以下规则。

如果我的托管对象实现了类似于IDisposable的某种关闭协议,我会在我持有引用的任何子COM对象上调用ReleaseComObject。我正在讨论的关闭协议的一些例子:

  • IObjectWithSite.SetSite(null)
  • IOleObject.SetClientSite(null)
  • IOleObject.Close()
  • IDTExtensibility2.OnDisconnection
  • IDTExtensibility2.OnBeginShutdown

  • IDisposable.Dispose本身

这有助于打破.NET和本机COM对象之间潜在的循环引用,因此托管垃圾收集器可以无阻碍地完成其工作。

也许,在VSTO互操作方案中可以使用类似的东西(AFAIR,IDTExtensibility2与此相关)。

如果互操作方案涉及IPC COM调用(例如,当您将托管事件接收器对象传递给Excel之类的进程外COM服务器时),则还有另一个选项可以跟踪对托管对象的外部引用:{{3 }}。 IExternalConnection::AddConnection / ReleaseConnectionIUnknown::AddRef / Release非常相似,但是当从另一个COM公寓(包括驻留在不同进程中的公寓)添加引用时,它们会被调用。 / p>

IExternalConnection提供了一种为out-of-proc场景实现几乎通用的关闭机制的方法。当外部引用计数达到零时,您应该在可能持有引用的任何外部Excel对象上调用ReleaseComObject,从而有效地破坏进程和Excel进程之间的任何潜在循环COM引用。也许,这样的事情已经由VSTO运行时实现(我对VSTO没有太多经验)。

那就是说,如果没有明确的关机机制,我就不会打电话给ReleaseComObject。另外,我从不使用FinalReleaseComObject

答案 1 :(得分:0)

如果您正在使用Office GUI,则不应忽略它!就像你的第二个链接一样:

  

必须释放对COM对象所做的每个引用。如果你不这样做,这个过程就会留在记忆中。

这意味着,如果您没有明确释放它们,您的对象将保留在内存中。由于它们是COM对象,因此垃圾收集器负责释放它们。但是,Excel和其他花哨的工具是在不了解.NET中的垃圾收集器的情况下实现的。它们与确定性释放记忆有关。如果您从excel请求对象并且未正确释放它,则可能是您的应用程序未正确关闭,因为它等待您的资源被释放。如果您的对象长期居住,应该进入gen1或gen2,那么这可能需要数小时甚至数天!

关于不释放COM对象的所有注意事项都是针对多线程场景或您必须在多个实例上推送许多com对象的场景。作为一个好建议,总是尽可能晚地创建您的com对象,并以与创建时相反的顺序尽快释放它们。此外,您应该考虑尽可能保持互操作实例的私密性。这样可以降低其他线程或实例在您释放对象时访问该对象的可能性。