.NET 和 COM 互操作故事






3.75/5 (3投票s)
让我们看一个使用 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 对象可能不是最佳方法。