如何在Silverlight 4中释放COM对象

如何在Silverlight 4中释放COM对象,silverlight,com,interop,Silverlight,Com,Interop,当与Office(通常是Excel)使用COM互操作时,我总是小心地确保对每个引用调用Marshal.ReleaseComObject,以避免Excel不退出的问题 当我从OOB Silverlight应用程序(使用AutomationFactory.CreateObject)使用互操作时,如何确保Excel退出 Silverlight没有封送.ReleaseComObject方法,甚至调用GC.Collect和GC.WaitForPendingFinalizers都没有帮助 如果没有发布COM

当与Office(通常是Excel)使用COM互操作时,我总是小心地确保对每个引用调用
Marshal.ReleaseComObject
,以避免Excel不退出的问题

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

Silverlight没有
封送.ReleaseComObject
方法,甚至调用
GC.Collect
GC.WaitForPendingFinalizers
都没有帮助

如果没有发布COM引用的机制,微软肯定没有向Silverlight添加此功能吗?在我看来,这似乎是自动化进程外COM服务器(如Excel)的一个障碍

一个令人惊讶的任务,尤其是皮特·布朗在其著作《Silverlight 4 in Action》的第5.5节中谈到了
AutomationFactory.CreateObject
,即:

此功能的主要目的是实现其他应用程序(包括Microsoft Office)的自动化

针对Hans的评论进行更新

我不相信“沉默刺客”问题存在于典型的办公应用程序自动化中。一个常见的用法可能类似于以下内容,我在WinForms应用程序中看到过多次使用,但从未遇到Hans链接的文章中描述的“有毒RCW”:

  • 创建一个Excel.Application实例
  • 打开或创建工作簿
  • 将数据写入工作簿
  • 显示Excel如果一切顺利,请关闭工作簿并调用应用程序。否则请退出
  • 调用Marshal.ReleaseComObject以释放所有Excel对象引用
未能按照Hans的建议调用Marshal.ReleaseComObject将导致多个Excel.exe副本运行,如上述KB文章中所述,这是非常不可取的

更新2

我用来重新编写的示例这是Pete Brown的书《Silverlight 4在行动中》的源代码示例,在这个页面上有一个下载链接。样品溶液AutomatingExcel位于Ch05.zip/5.03中。重编:

  • 确保没有Excel实例正在运行
  • 运行AutomatingExcel示例
  • 此时将打开Excel工作簿
  • 关闭Excel
  • 使用任务管理器观察Excel是否仍在运行
将所有动态变量设置为null并调用GC.Collect()似乎可以像AnthonyWJones的回答中指出的那样工作

更新2

御宅族的答案正是我想要的——通过在using语句中包装引用,COM引用将在不需要调用GC.Collect的情况下发布。一点实验表明,与上面提到的KB文章中描述的标准
Marshal.ReleaseComObject
解决方案不同,它更能容忍无法处理每个引用


<> P>一个权威的解决方案就是要确保所有Excel引用都被释放。

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


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

请查看以下代码:-

    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运行时的Office2007。然而,如果我们有一个变化,因为设置,然后我们在非常不稳定的地面上

编辑

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

    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进程继续运行

您可以实现
IDisposable
接口。我见过的最好的例子是在。博客条目是日文的,但在Chrome中打开,如果你不懂日文,可以让谷歌为你翻译网页

另外,如果您只想直接获得COM包装器的源代码示例,您可以下载它附带的示例应用程序:

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

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

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

此处需要的临界线如下:

((IDisposable)_excel).Dispose();  // Release COM Interface
这假定:

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

@Hans:Silverlight的可能副本在
封送
类中没有这两个方法。@Anthony-这解释了为什么这两个方法都不是好主意。“沉默刺客”链接非常相关。@Hans——“它解释了为什么这两种方法都不是好主意”——你能支持吗