我正在用C#编写一个应用程序,它将通过互操作打开一个Excel电子表格(目前是2007版),进行一些操作,然后关闭。 "魔法" 部分并不简单,因此该应用程序将包含许多对Excel生成的许多COM对象的引用。
我以前写过这种应用程序(实际上已经写了太多次了),但我从未找到一种舒适,"好闻" 的与COM对象交互的方法。问题部分在于,尽管进行了大量研究,我仍然不完全理解COM,而且互操作包装器隐藏了许多可能不应该被隐藏的内容。社区中有如此多不同的、相互冲突的建议只会使情况变得更糟。
如果你从标题中看不出来,那么我已经做过我的研究。标题暗示了这篇文章:
如何正确清理Excel互操作对象? 该建议最初是在2008年提出的,当时非常有帮助和可靠(特别是“永远不要使用2个点与com对象”这一点),但现在似乎已经过时了。在2010年3月,Visual Studio团队发布了一篇博客文章,警告其他程序员:Marshal.ReleaseComObject被认为是危险的。该文章引用了两篇文章:cbrumme的WebLog > ReleaseComObject和接口指针与运行时可调用包装器(RCW)之间的映射,暗示人们一直在错误地使用ReleaseComInterop(cbrumme:“如果您是使用少量COM对象的客户端应用程序,这些对象在您的托管代码中自由传递,您不应该使用ReleaseComObject”)。 是否有一个中等复杂度的应用程序示例,最好使用多个线程,能够成功地在内存泄漏(Excel在应用程序关闭后仍在后台运行)和InvalidComObjectExceptions之间进行导航?我正在寻找一种允许在创建它的上下文之外使用COM对象但仍然可以在应用程序完成后清理它的内存管理策略的混合体:一种有效跨越托管/非托管之间的内存管理策略。
参考文章或教程,讨论这个问题的正确方法,将是一个非常受欢迎的替代方案。我的最佳Google-fu努力返回了明显不正确的ReleaseComInterop方法。
我以前写过这种应用程序(实际上已经写了太多次了),但我从未找到一种舒适,"好闻" 的与COM对象交互的方法。问题部分在于,尽管进行了大量研究,我仍然不完全理解COM,而且互操作包装器隐藏了许多可能不应该被隐藏的内容。社区中有如此多不同的、相互冲突的建议只会使情况变得更糟。
如果你从标题中看不出来,那么我已经做过我的研究。标题暗示了这篇文章:
如何正确清理Excel互操作对象? 该建议最初是在2008年提出的,当时非常有帮助和可靠(特别是“永远不要使用2个点与com对象”这一点),但现在似乎已经过时了。在2010年3月,Visual Studio团队发布了一篇博客文章,警告其他程序员:Marshal.ReleaseComObject被认为是危险的。该文章引用了两篇文章:cbrumme的WebLog > ReleaseComObject和接口指针与运行时可调用包装器(RCW)之间的映射,暗示人们一直在错误地使用ReleaseComInterop(cbrumme:“如果您是使用少量COM对象的客户端应用程序,这些对象在您的托管代码中自由传递,您不应该使用ReleaseComObject”)。 是否有一个中等复杂度的应用程序示例,最好使用多个线程,能够成功地在内存泄漏(Excel在应用程序关闭后仍在后台运行)和InvalidComObjectExceptions之间进行导航?我正在寻找一种允许在创建它的上下文之外使用COM对象但仍然可以在应用程序完成后清理它的内存管理策略的混合体:一种有效跨越托管/非托管之间的内存管理策略。
参考文章或教程,讨论这个问题的正确方法,将是一个非常受欢迎的替代方案。我的最佳Google-fu努力返回了明显不正确的ReleaseComInterop方法。
更新:
(这不是一个答案)
我在发布后不久发现了这篇文章:
Jake Ginnivan的VSTO和COM互操作
我已经能够通过扩展方法实现他的“AutoCleanup”类包装COM对象的策略,并且对结果感到非常满意。虽然它并没有提供一种解决方案,允许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 );
}
}