到今天为止,使用 COM 对象的正确方法是什么? [英] As of today, what is the right way to work with COM objects?

查看:30
本文介绍了到今天为止,使用 COM 对象的正确方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个非常常见的问题,我决定提出这个问题,因为这个问题截至今天可能会有不同的答案.希望这些答案有助于理解使用 COM 对象的正确方法是什么.就我个人而言,在这个问题上得到不同的意见后,我感到很困惑.

This is a very common question and I decided to ask it because this question may have a different answer as of today. Hopefully, the answers will help to understand what is the right way to work with COM objects. Personally, I feel very confuse after getting different opinions on this subject.

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

The last 5 years, I used to work with COM objects and the rules were very clear for me:

  1. 在代码行中使用单个句点.使用多个周期会在幕后创建无法明确释放的临时对象.
  2. 不要使用 foreach,而是使用 for 循环并在每次迭代时释放每个项目
  3. 不要调用 FINalReleaseComObject,而是使用 ReleaseComObject.
  4. 不要使用 GC 来释放 COM 对象.GC Intent 主要用于调试用途.
  5. 以与创建对象相反的顺序释放对象.

在阅读最后几行之后,你们中的一些人可能会感到沮丧,这就是我对如何正确创建/释放 Com 对象的了解,我希望得到答案,使其更加清晰和无争议.

Some of you may be frustrated after reading those last lines, this is what I knew about how to properly create/release Com Object, I hope getting answers that will make it clearer and uncontested.

以下是我在此主题上找到的一些链接.他们中的一些人说需要调用 ReleaseComObject 而另一些人则不需要.

Following, are some links I found on this topic. Some of them telling that it is needed to call ReleaseComObject and some of them not.

  • How to properly release Excel COM objects (Nov. 2013)
  • Proper Way of Releasing COM Objects in .NET (Aug. 2011)
  • Marshal.ReleaseComObject Considered Dangerous (Mar. 2010)
  • ReleaseCOMObject (Apr. 2004)

...在 VSTO 场景中,您通常不必使用 ReleaseCOMObject...."

"... In VSTO scenarios, you typically don’t ever have to use ReleaseCOMObject. ..."

  • MSDN - Marshal.ReleaseComObject 方法(当前 .NET Framework 版本):
    • MSDN - Marshal.ReleaseComObject Method (current .NET Framework version):
    • ...您应该使用此方法来释放包含引用的底层 COM 对象..."

      "...You should use this method to free the underlying COM object that holds references..."

      更新:

      这个问题被标记为过于宽泛.根据要求,我会尽量简化并提出更简单的问题.

      This question has been marked as too broad. As requested, I will try to simplify and ask simpler questions.

      1. 在处理 COM 对象时是否需要 ReleaseComObject 或调用 GC 是正确的方法?
      2. VSTO 方法是否改变了我们过去使用 COM 对象的方式?
      3. 我写的上述规则中哪些是必需的,哪些是错误的?还有其他的吗?

      推荐答案

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

      The .NET / COM interop is well designed, and works correctly. In particular, the .NET Garbage Collector correctly tracks COM references, and will correctly release COM objects when they have no remaining runtime references. Interfering with the reference counts of COM object by calling Marshal.ReleaseComObject(...) or Marshal.FinalReleaseComObject(...) is a dangerous but common anti-pattern. Unfortunately, some of the bad advice came out of Microsoft.

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

      Your .NET code can correctly interact with COM while ignoring all 5 of your rules. If you do need to trigger deterministic clean-up of COM objects that are no longer referenced from the runtime, you can safely force a GC (and possibly wait for finalizers to complete). Otherwise, you don't have to do anything special in your code, to deal with COM objects.

      有一个重要的警告,这可能会导致对垃圾收集器角色的混淆.在调试.NET 代码时,局部变量人为地将它们的生命周期延长到方法的末尾,以支持在调试器下观察变量.这意味着您可能仍然拥有对 COM 对象的托管引用(因此 GC 不会清理),这比仅查看代码的预期形式晚.此问题(仅在调试器下发生)的一个很好的解决方法是将 COM 调用的范围与 GC 清理调用分开.

      There is one important caveat, that might have contributed to confusion about role of the garbage collector. When debugging .NET code, local variables artificially have their lifetime extended to the end of the method, in order to support watching the variabled under the debugger. That means you might still have managed references to a COM object (and hence the GC won't clean up) later than expect form just looking at the code. A good workaround for this issue (which only occurs under the debugger) is to split the scope of COM calls from the GC cleanup calls.

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

      As an example, here is some C# code that interacts with Excel, and cleans up properly. You can paste into a Console application (just add a reference to 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 进程正确关闭,表明所有 COM 对象都已正确清理.

      You'll see that the Excel process properly shuts down, indicating that all the COM objects were properly cleaned up.

      VSTO 不会改变任何这些问题 - 它只是一个包装和扩展本地 Office COM 对象模型的 .NET 库.

      VSTO does not change any of these issues - it is just a .NET library that wraps and extends the native Office COM object model.

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

      There is a lot of false information and confusion about this issue, including many posts on MSDN and on StackOverflow.

      最终说服我仔细研究并找出正确建议的是这篇文章https://blogs.msdn.microsoft.com/visualstudio/2010/03/01/marshal-releasecomobject-thinked-dangerous/ 以及通过引用查找问题在 StackOverflow 答案的调试器下保持活动状态.

      What finally convinced me to have a closer look and figure out the right advice was this post https://blogs.msdn.microsoft.com/visualstudio/2010/03/01/marshal-releasecomobject-considered-dangerous/ together with finding the issue with references kept alive under the debugger on a StackOverflow answer.

      此一般指南的一个例外是 COM 对象模型要求以特定顺序释放接口.此处描述的 GC 方法无法让您控制 GC 释放 COM 对象的顺序.

      One exception to this general guidance is when the COM object model requires interfaces to be released in a particular order. The GC approach described here does not give you control over the order in which the COM objects are released by the GC.

      我没有任何参考资料表明这是否会违反 COM 合同.通常,我希望 COM 层次结构使用内部引用来确保正确管理对序列的任何依赖关系.例如.在 Excel 的情况下,人们希望 Range 对象保持对父 Worksheet 对象的内部引用,因此对象模型的用户无需显式地保持两者都处于活动状态.

      I don't have any reference to indicate whether this would violate the COM contract. In general, I would expect COM hierarchies to use internal references to ensure any dependencies on the sequence are properly managed. E.g. in the case of Excel, one would expect a Range object to keep an internal reference to the parent Worksheet object, so that a user of the object model need not explicitly keep both alive.

      在某些情况下,即使是 Office 应用程序也对 COM 对象的释放顺序敏感.一种情况似乎是使用 OLE 嵌入时 - 请参阅 https://blogs.msdn.microsoft.com/vsofficedeveloper/2008/04/11/excel-ole-embedding-errors-if-you-have-managed-add-in-sinking-application-events-in-excel-2/

      There may be cases where even the Office applications are sensitive to the sequence in which COM objects are released. One case seems to be when the OLE embedding is used - see https://blogs.msdn.microsoft.com/vsofficedeveloper/2008/04/11/excel-ole-embedding-errors-if-you-have-managed-add-in-sinking-application-events-in-excel-2/

      因此,如果对象以错误的顺序释放,则可能会创建一个失败的 COM 对象模型,并且与此类 COM 模型的互操作则需要更加小心,并且可能需要手动干预引用.

      So it would be possible to create a COM object model that fails if objects are released in the wrong sequence, and the interop with such a COM model would then require some more care, and might need manual interference with the references.

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

      But for general interop with the Office COM object models, I agree with the VS blog post calling "Marshal.ReleaseComObject – a problem disguised as a solution".

      这篇关于到今天为止,使用 COM 对象的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆