2010-05-28 78 views
27

編輯是否需要使用Marshal.ReleaseComObject釋放*每個* Excel互操作對象?

還請How do I properly clean up Excel interop objects?看到的。我最近遇到了這個問題,它提供了很多關於如何正確處理COM對象的問題。絕對檢查超出第一個(標記的)答案,因爲其他答案超出了簡單的「不要使用兩個點」和「爲每個COM對象使用ReleaseComObject」的建議。

我首先重新討論了這個問題,因爲我意識到儘管在註冊和處理所有COM對象方面非常全面,但我的Excel實例仍然沒有正確處理。事實證明,有很多方法可以創建完全不明顯的COM對象(即,即使您從不使用兩個點,也可能會錯過COM對象)。此外,即使你是徹底的,如果你的項目增長超過一定的規模,錯過一個COM對象的機會接近100%。當發生這種情況時,很難找到你錯過的那個。上面鏈接的問題的答案提供了一些確保Excel實例完全關閉的其他技巧。同時,我對ComObjectManager(下文)做了一個小的(但是很重要的)更新,以反映我從上面提到的問題中學到了什麼。

原始的問題

我見過幾個例子Marshal.ReleaseComObject()使用與Excel互操作對象(即,從命名空間中的Microsoft.Office.Interop.Excel對象),但我已經看到了使用各種度。

我想知道如果我能逃脫這樣的:

var application = new ApplicationClass(); 
try 
{ 
    // do work with application, workbooks, worksheets, cells, etc. 
} 
finally 
{ 
    Marashal.ReleaseComObject(application) 
} 

或者,如果我需要釋放每一個對象創建的,在這個方法:

public void CreateExcelWorkbookWithSingleSheet() 
{ 
    var application = new ApplicationClass(); 
    var workbook = application.Workbooks.Add(_missing); 
    var worksheets = workbook.Worksheets; 
    for (var worksheetIndex = 1; worksheetIndex < worksheets.Count; worksheetIndex++) 
    { 
     var worksheet = (WorksheetClass)worksheets[worksheetIndex]; 
     worksheet.Delete(); 
     Marshal.ReleaseComObject(worksheet); 
    } 
    workbook.SaveAs(
     WorkbookPath, _missing, _missing, _missing, _missing, _missing, 
     XlSaveAsAccessMode.xlExclusive, _missing, _missing, _missing, _missing, _missing); 
    workbook.Close(true, _missing, _missing); 
    application.Quit(); 
    Marshal.ReleaseComObject(worksheets); 
    Marshal.ReleaseComObject(workbook); 
    Marshal.ReleaseComObject(application); 
} 

是什麼促使我問這個問題是,作爲LINQ的奉獻者,我真的很想做這樣的事情:

var worksheetNames = worksheets.Cast<Worksheet>().Select(ws => ws.Name); 

...但我擔心如果我不釋放每個工作表(ws)對象,我將最終發生內存泄漏或幽靈進程。

任何有關這方面的意見將不勝感激。

更新

基於答案爲止,這聽起來像我確實需要釋放每一個COM對象創建。我藉此機會創建了一個ComObjectManager課程,以便處理這個令人頭疼的問題。每次你實例化一個新的com對象時,你都必須記得使用Get()方法,但如果你這樣做,它會爲你處理所有其他事情。如果您發現任何問題,請告訴我(或編輯並留下評論,如果可以的話)。下面的代碼:

public class ComObjectManager : IDisposable 
{ 
    private Stack<object> _comObjects = new Stack<object>(); 

    public TComObject Get<TComObject>(Func<TComObject> getter) 
    { 
     var comObject = getter(); 
     _comObjects.Push(comObject); 
     return comObject; 
    } 

    public void Dispose() 
    { 
     // these two lines of code will dispose of any unreferenced COM objects 
     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 

     while (_comObjects.Count > 0) 
      Marshal.ReleaseComObject(_comObjects.Pop()); 
    } 
} 

下面是一個使用示例:

public void CreateExcelWorkbookWithSingleSheet() 
{ 
    using (var com = new ComObjectManager()) 
    { 
     var application = com.Get<ApplicationClass>(() => new ApplicationClass()); 
     var workbook = com.Get<Workbook>(() => application.Workbooks.Add(_missing)); 
     var worksheets = com.Get<Sheets>(() => workbook.Worksheets); 
     for (var worksheetIndex = 1; worksheetIndex < worksheets.Count; worksheetIndex++) 
     { 
      var worksheet = com.Get<WorksheetClass>(() => (WorksheetClass)worksheets[worksheetIndex]); 
      worksheet.Delete(); 
     } 
     workbook.SaveAs(
      WorkbookPath, _missing, _missing, _missing, _missing, _missing, 
      XlSaveAsAccessMode.xlExclusive, _missing, _missing, _missing, _missing, _missing); 
     workbook.Close(true, _missing, _missing); 
     application.Quit(); 
    } 
} 
+0

對於負責釋放對象的ComObjectManager類+1。 – 2011-03-25 06:25:10

+0

但是,由於編譯器在後臺創建大量匿名方法,因此ComObjectManager的解決方案可能會導致程序集的大小增加。 – 2011-03-25 06:50:53

+0

與經理的好主意。 – 2011-04-01 13:47:51

回答

13

我相信你將不得不調用ReleaseComObject的每一個COM對象。由於它們不是垃圾收集,所以父子層次並沒有真正進入等式:即使你釋放父對象,它也不會減少任何子對象的引用計數。

+0

只是想到了一些......這是否適用於返回值類型的Excel Interop函數?例如'myCell.get_Address()'返回一個'string'。 'myColumn.AutoFit()'返回一個'bool'。我能否認爲這些不是問題? – devuxer 2010-05-28 21:49:51

+4

是的,你可以假設這些都不是問題。你只需要明確地釋放COM對象;標準的.NET類型很好。 (尼斯在你的包裝順便說一句,非常優雅的解決方案。) – 2010-05-29 13:09:02

+0

謝謝,謝謝! :) – devuxer 2010-05-29 17:59:19

6

您應該在代碼中使用的每個COM對象上調用Marshal.ReleaseComObject,而不僅僅是主應用程序對象。

相關問題