到目前为止,使用COM对象的正确方法是什么?

这是一个非常普遍的问题,我决定提出这个问题,因为这个问题可能与今天有不同的答案。 希望这些答案将有助于理解什么是使用COM对象的正确方法。 就个人而言,在对这个问题有不同的看法后,我感到非常困惑。

过去5年来,我曾经使用COM对象,规则对我来说非常清楚:

  1. 在代码行中使用单个句点。 使用多个句点在场景后面创build不能被明确释放的临时对象。
  2. 不要使用foreach,而是使用for循环,并在每次迭代中释放每个项目
  3. 不要调用FInalReleaseComObject,而是使用ReleaseComObject。
  4. 不要使用GC释放COM对象。 GC意图主要用于debugging使用情况。
  5. 以与创build相反的顺序释放对象。

读完这些最后一行后,你们中的一些人可能会感到沮丧,这就是我如何正确地创build/释放Com Object所知道的,我希望得到的答案能够使它更加清晰和无可争议。

以下是我在这个主题上find的一些链接。 其中一些人告诉说需要调用ReleaseComObject,而其中一些则不是。

  • 如何正确释放Excel COM对象 (2013年11月)
  • 在.NET中释放COM对象的正确方法 (2011年8月)
  • Marshal.ReleaseComObject认为危险 (2010年3月)
  • ReleaseCOMObject (2004年4月)

“…在VSTO场景中,你通常不需要使用ReleaseCOMObject …”

  • MSDN – Marshal.ReleaseComObject方法 (当前的.NET Framework版本):

“…您应该使用此方法释放包含引用的基础COM对象…”

更新:

这个问题被标记为太宽泛。 按照要求,我会尽量简化并提出更简单的问题。

  1. 使用COM对象时是否需要ReleaseComObject或调用GC是正确的方法?
  2. VSTO方法是否改变了我们用COM对象的方式?
  3. 我写的上面哪个规则是必需的,哪个是错的? 还有别的吗?

.NET / COM互操作性良好,能够正常工作。 尤其是,.NET垃圾收集器正确地跟踪COM引用,并且在没有剩余运行时引用时将正确释放COM对象。 通过调用Marshal.ReleaseComObject(...)Marshal.FinalReleaseComObject(...)来干扰COM对象的引用计数是一种危险但常见的反模式。 不幸的是,一些糟糕的build议来自微软。

您的.NET代码可以正确地与COM交互,而忽略所有5个规则。 如果确实需要触发不再从运行时引用的COM对象的确定性清除,则可以安全地强制GC(并可能等待终结器完成)。 否则,你不必在代码中做任何特殊的事情来处理COM对象。

有一个重要的警告,这可能会导致垃圾收集器的angular色混淆。 当debugging.NET代码时,局部variables人为地将其生存期延长到方法的末尾,以支持在debugging器下观察variables。 这意味着你可能仍然有一个COM对象的引用(因此GC不会清理),比期望的forms只看代码。 这个问题(只发生在debugging器下)的一个很好的解决方法是从GC清除调用中分离COM调用的作用域。

作为一个例子,下面是一些与Excel交互的C#代码,并正确清理。 您可以粘贴到控制台应用程序(只需添加对Microsoft.Office.Interop.Excel的引用):

 using System; using System.Runtime.InteropServices; using Microsoft.Office.Interop.Excel; namespace TestCsCom { class Program { static void Main(string[] args) { // NOTE: Don't call Excel objects in here... // Debugger would keep alive until end, preventing GC cleanup // Call a separate function that talks to Excel DoTheWork(); // Now let the GC clean up (repeat, until no more) do { GC.Collect(); GC.WaitForPendingFinalizers(); } while (Marshal.AreComObjectsAvailableForCleanup()); } static void DoTheWork() { Application app = new Application(); Workbook book = app.Workbooks.Add(); Worksheet worksheet = book.Worksheets["Sheet1"]; app.Visible = true; for (int i = 1; i <= 10; i++) { worksheet.Cells.Range["A" + i].Value = "Hello"; } book.Save(); book.Close(); app.Quit(); // NOTE: No calls the Marshal.ReleaseComObject() are ever needed } } } 

您将看到Excel进程正常closures,表明所有COM对象都已正确清理。

VSTO不会改变任何这些问题 – 它只是一个.NET库,它包装和扩展了原生的Office COM对象模型。


关于这个问题有很多错误的信息和混淆,包括MSDN和StackOverflow上的许多post。

什么最终说服我有一个更仔细的审视,并找出正确的build议是这个职位https://blogs.msdn.microsoft.com/visualstudio/2010/03/01/marshal-releasecomobject-considered-dangerous/一起寻找在StackOverflow答案的debugging器下保持活动的问题&#x3002;


这个通用指南的一个例外是COM对象模型需要以特定顺序发布接口。 这里描述的GC方法不能控制GC释放COM对象的顺序。

我没有任何资料可以说明这是否会违反COM合约。 一般来说,我希望COM层次结构使用内部引用来确保顺序上的任何依赖关系得到正确的pipe理。 例如在Excel的情况下,人们会期望一个Range对象保持对父Worksheet对象的内部引用,这样对象模型的用户不需要明确地保持两者都活着。

有些情况下,即使Office应用程序对COM对象的释放顺序也很敏感。 一种情况似乎是在使用OLEembedded时 – 请参阅https://blogs.msdn.microsoft.com/vsofficedeveloper/2008/04/11/excel-ole-embedding-errors-if-you-have-managed-add -in-下沉应用事件合的Excel-2 /

因此,如果对象以错误的顺序被释放,创build一个COM对象模型将失败,并且与这种COM模型的互操作将需要更多的关注,并且可能需要手动干涉参考。

但是对于与Office COM对象模型的一般互操作,我同意VS博客文章中称为“Marshal.ReleaseComObject – 一个伪装成解决scheme的问题”。