2011-04-09 29 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");