65.9K
CodeProject 正在变化。 阅读更多。
Home

.NET 和 COM 互操作故事

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.75/5 (3投票s)

2009年6月2日

CPOL

2分钟阅读

viewsIcon

17134

让我们看一个使用 Internet Explorer 暴露的 COM 对象的简单示例。下面的代码会更改 HTML 文档中每个链接的颜色。

.NET 允许程序员在托管代码中重用 COM 组件。为了实现这一点,需要在本机对象周围创建一个托管包装器对象。除此之外,你可以像使用任何其他托管对象一样使用 COM 对象。即使听起来很简单,你也必须意识到 CLR 的对象生命周期管理和 COM 版本的对象生命周期管理之间的差异。

COM 程序员必须对每个被 AddRef'ed 的接口调用 Release。对于使用 COM 对象的 C# 程序员来说,这意味着 AddRef 会在

  • 创建 COM 对象时
  • 通过调用方法或属性返回 COM 对象时
  • 将 COM 对象转换为另一种 COM 接口类型时

在 C# 中释放 COM 对象,有两种选择

  • 让 GC 收集托管包装器并调用它们的终结器,这些终结器将在本机 COM 对象上调用 Release
  • 手动对代码中使用的每个接口调用 Marshal.ReleaseComObject

让我们看一个使用 Internet Explorer 暴露的 COM 对象的简单示例。下面的代码会更改 HTML 文档中每个链接的颜色。

// IHTMLDocument2 doc;
foreach (IHTMLElement elem in doc.all)
{
    IHTMLAnchorElement anchor = elem as IHTMLAnchorElement;
    if (anchor != null)
    {
        elem.style.color = "red";
    }
}

第一种方法将释放 COM 对象的任务交给垃圾回收器。现在让我们手动释放 COM 对象

// IHTMLDocument2 doc;
IHTMLElementCollection allCollection = doc.all;
foreach (IHTMLElement crntElem in allCollection)
{
   IHTMLAnchorElement anchor = crntElem as IHTMLAnchorElement;
   if (anchor != null)
   {
       IHTMLStyle style = crntElem.style;
       style.color = "red";

       Marshal.ReleaseComObject(style);
       Marshal.ReleaseComObject(anchor);
   }

   Marshal.ReleaseComObject(crntElem);
}

Marshal.ReleaseComObject(allCollection);

正如你所看到的,代码行数翻倍了!我个人更喜欢将释放 COM 对象的任务交给 GC,即使它们最终会在 GC 启动时过一段时间后才被释放。

有些人可能会尝试在处理 COM 对象的大量代码块后调用 GC.Collect,但这可能会更糟,因为其他托管对象可能会被提升到下一代 GC,从而延长了它们的生命周期,而这并非必要。

理论上,有可能创建大量的 COM 对象,这些对象会超出本机堆,而托管堆却有很多可用内存,因为托管包装器的大小更小。在这种情况下,GC 不会被调用,因此本机堆不会被释放。


如果你的应用程序遇到这种内存分配问题,那么从托管代码中使用 COM 对象可能不是最佳方法。

© . All rights reserved.