我正在用 C# 编写一个应用程序,它将通过互操作打开一个 Excel 电子表格(目前是 2007 年),做一些魔术,然后关闭。“神奇”部分并不重要,因此该应用程序将包含对 Excel 生成的许多 COM 对象的许多引用。
我以前写过这种应用程序(实际上写了太多次),但我从来没有找到一种舒适的、“好闻”的方法来与 COM 对象交互。问题的部分原因在于,尽管进行了大量研究,但我仍然不能完全理解 COM,部分原因在于互操作包装器隐藏了很多可能不应该隐藏的内容。来自社区的许多不同的、相互矛盾的建议只会让事情变得更糟。
如果你不能从标题中看出,我已经完成了我的研究。标题暗示了这篇文章:
如何正确清理 Excel 互操作对象?
2008 年第一次被问到,这个建议在当时真的很有帮助而且很可靠(尤其是“永远不要在 com 对象中使用 2 点”位),但现在似乎已经过时了。2010 年 3 月,Visual Studio 团队发布了一篇博客文章,警告其他程序员Marshal.ReleaseComObject [is] 被认为是危险的。文章参考了两篇文章,cbrumme 的 WebLog > ReleaseComObject和The mapping between interface pointers and runtime callable wrappers (RCWs),暗示人们一直在错误地使用ReleaseComInterop(cbrumme:“如果你是一个客户端应用程序,使用的数量不多在您的托管代码中自由传递的 COM 对象,您不应使用 ReleaseComObject")。
有没有人有一个中等复杂的应用程序的例子,最好使用多个线程,它能够成功地在内存泄漏(应用程序关闭后 Excel 继续在后台运行)和 InvalidComObjectExceptions 之间导航?我正在寻找允许 COM 对象在创建它的上下文之外使用但在应用程序完成后仍然可以清理的东西:一种可以有效跨越托管的内存管理策略的混合体/非托管划分。
参考讨论此问题的正确方法的文章或教程将是一个非常受欢迎的替代方案。我最好的 Google-fu 努力返回了明显不正确的 ReleaseComInterop 方法。
更新:(
这不是答案)
我在发布后不久发现了这篇文章:
Jake Ginnivan 的 VSTO 和 COM 互操作
我已经能够通过扩展方法实现他将 COM 对象包装在“AutoCleanup”类中的策略,我对结果非常满意。尽管它没有提供允许 COM 对象跨越创建它们的上下文边界并且仍然使用 ReleaseComObject 函数的解决方案,但它至少提供了一个简洁且易于阅读的解决方案。
这是我的实现:
class AutoCleanup<T> : IDisposable {
public T Resource {
get;
private set;
}
public AutoCleanup( T resource ) {
this.Resource = resource;
}
~AutoCleanup() {
this.Dispose();
}
private bool _disposed = false;
public void Dispose() {
if ( !_disposed ) {
_disposed = true;
if ( this.Resource != null &&
Marshal.IsComObject( this.Resource ) ) {
Marshal.FinalReleaseComObject( this.Resource );
} else if ( this.Resource is IDisposable ) {
( (IDisposable) this.Resource ).Dispose();
}
this.Resource = null;
}
}
}
static class ExtensionMethods {
public static AutoCleanup<T> WithComCleanup<T>( this T target ) {
return new AutoCleanup<T>( target );
}
}