如何在Silverlight 4中释放COM对象

时间:2011-04-09 12:28:24

标签: silverlight com interop

当使用COM Interop with Office(通常为Excel)时,我始终小心确保在每个引用上调用Marshal.ReleaseComObject,以避免Excel不退出as described in this KB article的问题。

当我从OOB Silverlight应用程序(使用AutomationFactory.CreateObject)使用Interop时,如何确保Excel退出?

Silverlight没有Marshal.ReleaseComObject方法,甚至调用GC.CollectGC.WaitForPendingFinalizers也无济于事。

当然,如果没有释放COM引用的机制,Microsoft是否还没有将此功能添加到Silverlight中?在我看来,这似乎是自动化进程外COM服务器(例如Excel)的一个显示器。

令人惊讶的遗漏,尤其是皮特·布朗在他的书“Silverlight 4 in Action”第5.5节中提到的AutomationFactory.CreateObject,即:

  

此功能的主要目的是允许自动化其他应用程序,包括Microsoft Office。

更新以回应Hans的评论。

我不相信Office应用程序的典型自动化中存在“无声刺客”问题。一个常见的用法可能看起来像下面这样,我已经看到在WinForms应用程序中反复使用,而没有遇到由Hans链接的文章中描述的“中毒的RCW”:

  • 创建Excel.Application实例
  • 打开或创建工作簿
  • 将数据写入工作簿
  • 如果一切顺利,则显示Excel,关闭工作簿并调用Application.Quit,如果没有。
  • 调用Marshal.ReleaseComObject以释放所有Excel对象引用。

未按照Hans的建议调用Marshal.ReleaseComObject会使Excel.exe的多个副本运行,如上面提到的知识库文章所述 - 非常不受欢迎。

更新2

我正在使用的示例是来自Pete Brown的书Silverlight 4的源代码示例,该页面上有一个下载链接。示例解决方案AutomatingExcel位于Ch05.zip / 5.03中。重申:

  • 确保没有正在运行的Excel实例
  • 运行AutomatingExcel示例
  • 打开Excel工作簿
  • 关闭Excel
  • 使用任务管理器观察Excel仍在运行。

将所有动态变量设置为null并调用GC.Collect()似乎在AnthonyWJones的回答中指出。

更新2

Otaku的答案正是我所寻找的 - 通过在using语句中包装引用,COM引用被释放而无需调用GC.Collect。一些实验表明,与上面引用的知识库文章中描述的标准Marshal.ReleaseComObject解决方案不同,它更能容忍未能处理每一个引用。

对于确保所有Excel引用都已发布必须处理的内容具有权威性,将会很有趣。

4 个答案:

答案 0 :(得分:2)

您可以实现IDisposable界面。我见过的最好的例子是http://csfun.blog49.fc2.com/blog-entry-79.html。博客文章是日文版,但在Chrome中打开,如果您不阅读日文,Google会为您翻页。

此外,如果您只想直接使用COM包装器的源代码示例,则可以下载它所带来的示例应用程序:SilverOffice

答案 1 :(得分:1)

看看这段代码: -

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        dynamic app = AutomationFactory.CreateObject("Excel.Application");
        dynamic book = app.Workbooks.Add();
        dynamic sheet = app.ActiveSheet();

        sheet = null;
        book.Close(false);
        book = null;
        app.Quit();
        app = null;

        GC.Collect();
    }

Excel进程出现,然后消失。删除GC,Excel进程将继续。如果你逐字复制这段代码,你会得到同样的结果吗?如果是这样,那么它会建议代码中的某个地方仍然可以从其中一个线程堆栈或静态字段访问对excel对象的引用。

你是否曾在一个字段中持有一个excel对象(而不是局部变量)?

您是否在看似可变的内容中保存了一个excel对象,但是是从用作事件处理程序的动态委托或lambda引用的?

您是否将事件处理程序附加到寿命较短的对象中的长寿命对象?如果是这样,你是否确保正确地脱离这些处理程序?

这些事情中的许多都可以让开发人员离开他们认为对象准备好的GC,但GC发现它们是可访问的,因此不适合收集。

如果上面的代码行为不一样,那么我们就完全在寻找另一个问题了。我在最新的SL 4运行时在Server 2008 R2上使用Office 2007。然而,如果我们因为设置而变化,那么我们就处于非常不稳定的状态。

修改

通过一些测试,这似乎是有效的: -

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        using (dynamic app = AutomationFactory.CreateObject("Excel.Application"))
        {
            using (dynamic book = app.Workbooks.Add())
            {
                using (dynamic sheet = app.ActiveSheet())
                {

                }
                book.Close();
            }
            app.Quit();
        };

        GC.Collect();
    }

但是,请不要使用GC,最终会出现不需要的Excel进程。

答案 2 :(得分:0)

我会考虑在WCF服务中构建Excel文件。你可以在那里做所有的清理工作。发送到您的Silverlight应用程序的字节,并使用SaveFileDialog将它们发送给用户。

Silverlight对访问客户端文件系统有限制。在客户端系统上操作Excel文件,或者甚至假设客户端安装了Excel,这似乎违反了这一点。

答案 3 :(得分:0)

  

通过在using语句中包装引用来释放COM引用

请注意:using语句只是try / catch / finally的语法糖,在finally子句中使用Dispose()。

此外,大多数应用程序不允许在这种情况下使用语句,因为COM对象的创建和清理是在各种位置/方法之间分配的。

这里需要的关键线是:

((IDisposable)_excel).Dispose();  // Release COM Interface

这假定:

dynamic _excel = AutomationFactory.CreateObject("Excel.Application");