2011-04-09 58 views
6

与Office(通常是Excel)一起使用COM Interop时,我总是小心地确保在每次引用时都拨打Marshal.ReleaseComObject,以避免Excel无法退出的问题as described in this KB article如何在Silverlight 4中释放COM对象

如何在使用OOB Silverlight应用程序中的Interop时(AutomationFactory.CreateObject)确保Excel退出?

Silverlight没有Marshal.ReleaseComObject方法,甚至拨打GC.CollectGC.WaitForPendingFinalizers也没有帮助。

当然,Microsoft没有将此功能添加到Silverlight,而没有一种机制来释放COM引用?这对我来说似乎是自动化进程外COM服务器(如Excel)的不二之选。

一个令人惊讶的ommission,更何况作为皮特·布朗在他的著作“在行动的Silverlight 4”第5.5节竟把要说AutomationFactory.CreateObject,即:

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

UPDATE回应汉斯的意见。

我不相信在典型的Office应用程序自动化中存在“无声刺客”问题。一个常见的用途可能类似于下面,我已经看到了的WinForms应用程序重复使用,而不需要通过不断来“毒害RCW”的文章由汉斯链接中描述:

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

如Hans所推荐,未能调用Marshal.ReleaseComObject会导致运行Excel.exe的多个副本,如上面提到的知识库文章中所述 - 这是非常不理想的。

更新2

我使用瑞普这是由皮特·布朗的书的Silverlight 4在行动的源代码样本的样本,有此页面上的下载链接。示例解决方案AutomatingExcel位于Ch05.zip/5.03中。要瑞普:

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

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

更新2

宅男的答案是什么,我一直在寻找 - 通过在using语句的COM引用,而无需调用GC.Collect释放包裹引用。一些实验表明,与上面引用的知识库文章中描述的标准Marshal.ReleaseComObject解决方案不同,它更容易处理每个单独的引用。

确切地说必须处理什么以确保所有Excel引用都已发布,这将是一件有趣的事情。

+0

的可能重复的[?何时使用RelaseComObject VS FinalReleaseComObject](http://stackoverflow.com/ question/3937181/when-to-use-relasecomobject-vs-finalreleasecomobject) – 2011-04-09 13:28:36

+0

@Hans:Silverlight在Marshal类中没有这些方法。 – AnthonyWJones 2011-04-09 13:57:35

+0

@Anthony - 它解释了为什么这些方法都不是一个好主意。 “无声刺客”链接非常相​​关。 – 2011-04-09 14:01:10

回答

2

您可以实现IDisposable接口。我见过的最好的例子是http://csfun.blog49.fc2.com/blog-entry-79.html。博客文章是日文的,但是在Chrome中打开,如果你没有阅读日文,让Google为你做页面翻译。另外,如果您只是想直接使用COM包装器的源代码示例,则可以下载示例应用程序:SilverOffice

+0

+1谢谢 - 这是我正在寻找的答案 - 如何在不调用GC.Collect的情况下发布COM对象。看起来用dynamic关键字创建的引用实现了IDisposable,并且这释放了COM引用。可惜MSDN并没有提供有关此主题的更多明确信息:http://msdn.microsoft.com/en-us/library/dd264733.aspx – Joe 2011-05-13 19:52:11

+0

这很有趣我没有想过测试和使用'IDisposable'不幸的是,处置不会释放COM对象。 – AnthonyWJones 2011-05-13 20:15:53

+0

@Joe:我刚刚通过将Dispose添加到我的代码中进行了测试。没有GC.Collect,我仍然会得到多个Excel实例。 – AnthonyWJones 2011-05-13 20:17:30

0

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

Silverlight对访问客户端文件系统有限制。操作客户端系统上的Excel文件,或者甚至假定客户端安装了Excel似乎违反了此规定。

+0

虽然你的方法是有效的,但它不回答这个问题。并且可能有合法的理由希望在提升的信任OOB应用程序中使客户端的Excel自动化。 – Joe 2011-05-13 08:36:04

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的过程。

+0

看起来很有希望。我的目标环境是Excel 2003,但我刚刚在家中的Excel 2007上尝试了您的代码,并且它可以正常工作。设置变量为空似乎是必需的,不知道为什么,因为他们应该无法访问。尽管如此,GC.Collect()似乎只是为了释放COM引用而使用重炮。 – Joe 2011-05-13 16:16:01

+0

@Joe:不一定,如果您从我的代码中删除'x = null',那么在调用GC.Collect时,尽管调用了GC.Collect,仍可以继续执行Excel过程。这是因为当Collect被调用时,所有线程都被挂起,并且GC会检查它们的堆栈,它可能会在其中找到本地变量所持有的引用。我使用“可能”一词,因为优化会使它们无法访问。如果将大多数代码移动到另一个函数中并从Click事件中调用该代码,则在不需要分配给null之后调用GC.Collect。 – AnthonyWJones 2011-05-13 17:01:29

+0

+1,并感谢您的持续调查。你的代码可能有一个隐含的实例化(“双点”问题)。你有没有试过“使用(动态工作簿= app.Workbooks)”,后面跟着“workbooks.Add()”,以确保所有引用都被显式实例化和处理。使用标准的COM Interop小心地做到这一点是必要的,否则可能会解释为什么最终会让进程运行。 – Joe 2011-05-14 08:08:01

0

通过在using语句包裹引用COM引用被释放

请注意:using语句是的try/catch /最后用的Dispose()的最后条款中只是语法糖。

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

其在此所需要的临界线读取:

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

这假定:

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