2012-03-24 196 views
0

可以說我有一個組件正在做一些Workbook對象,並且在那個方法體的中間某處我調用了另一個類的某個方法。 例如:COM對象excel互操作清理

public class MainComponent 
{ 

    public void MyMainMethod() 
    { 
     OtherComponent otherComponent = new OtherComponent(); 
     Workbook document; 
     // some work with workbook object 

     // working with document and worksheet objects. 
     otherComponent.MethodCall(document); 

     // some work with workbook object and it's worksheets. 

     foreach(Worksheet sheet in document.Workheets) 
     // do something with sheet 
    } 
} 

public class OtherComponent 
{ 
    public void MethodCall(Workbook document) 
    { 
    string worksheetNames = ""; 
    foreach(Worksheet sheet in document.Worksheets) 
     worksheetNames += sheet.Name; 
    Console.WriteLine(worksheetNames); 
    } 
} 

在那otherComponent.MethodCall(文件);我正在使用文檔,並且正在遍歷它的工作表。

編輯更具體的問題。我是否應該在文檔和其他Component.MethodCall(文檔)中的Worksheet上調用ReleaseCOMObject?

我從來沒有真正有什麼好的解釋,應該如何管理這個非託管代碼。 我真的很感激,如果有人可以向我解釋這一點。

+0

通常,創建對象的方法應負責清理。在這種情況下,包含「清理」的內容有些模糊。你應該發佈初始化和清理代碼,以及更多關於你在做什麼的解釋,「以後可能會導致問題」。 – 2012-03-24 13:24:13

回答

4

您必須在創建它們的範圍內手動釋放所有本地對象。當通過自動化使用Office應用程序時,不要依賴垃圾收集器清理這些對象 - 即使它正確無誤,也可能需要一些時間才能啓動,並且最終可能會有臨時對象持有對其他對象的引用你認爲已經走了。

This is a somewhat related question更多詳細信息可能適用於您,如果您嘗試從隱藏Excel的應用程序運行Excel。

這是給你絕對的有關部分是這樣的:

  • 裹使用Excel中的try..catch塊捕捉到任何可能的例外每一個功能。
  • 總是通過調用Marshal.ReleaseComObject()明確釋放所有Excel對象,然後在您不需要它們時立即將變量設置爲null。始終在finally塊中釋放這些對象以確保失敗的Excel方法調用不會導致懸空的COM對象。
  • 發生錯誤時,請關閉您正在使用的Excel實例。您不可能從與Excel相關的錯誤中恢復,並且保留該實例的時間越長,使用資源的時間越長。
  • 當您退出Excel時,請確保您防範遞歸調用的代碼 - 如果您的異常處理程序嘗試在代碼已關閉Excel的過程中關閉Excel,則最終會生成一個死的Excel實例。
  • 調用和GC.WaitForPendingFinalizers()之後立即調用Application.Quit()方法以確保.NET Framework立即釋放所有Excel COM對象。

編輯這是你增加了更多的細節,你的問題後。

otherComponent你不需要釋放WorkbookDocument對象。這兩個對象是在第一個對象中創建的,這意味着第一個對象是所有者。由於它是擁有頂級Excel對象的第一個對象(假設某個地方還有一個Application對象),您的第一個對象可以調用otherComponent,傳入WorkbookDocument,然後返回時清理它們。如果您從未在MainComponent中使用這些對象中的任何一個,那麼您應該在otherComponent內創建與Excel相關的對象並在那裏清理它們。

使用COM互操作,您應該創建COM對象儘可能靠近需要它們的位置,並儘快顯式釋放它們。對於Office應用程序尤其如此。

我讓這個類更容易使用COM對象:這個包裝是一次性的,所以你可以用你的COM對象使用using(...) - 當using作用域結束時,包裝器釋放COM對象。

using System; 
using System.Runtime.InteropServices; 

namespace COMHelper 
{ 
    /// <summary> 
    /// Disposable wrapper for COM interface pointers. 
    /// </summary> 
    /// <typeparam name="T">COM interface type to wrap.</typeparam> 
    public class ComPtr<T> : IDisposable 
    { 
     private object m_oObject; 
     private bool m_bDisposeDone = false; 

     /// <summary> 
     /// Constructor 
     /// </summary> 
     /// <param name="oObject"></param> 
     public ComPtr (T oObject) 
     { 
      if (oObject == null) 
       throw (new ArgumentNullException ("Invalid reference for ComPtr (cannot be null)")); 

      if (!(Marshal.IsComObject (oObject))) 
       throw (new ArgumentException ("Invalid type for ComPtr (must be a COM interface pointer)")); 

      m_oObject = oObject; 
     } 

     /// <summary> 
     /// Constructor 
     /// </summary> 
     /// <param name="oObject"></param> 
     public ComPtr (object oObject) : this ((T) oObject) 
     { 
     } 

     /// <summary> 
     /// Destructor 
     /// </summary> 
     ~ComPtr() 
     { 
      Dispose (false); 
     } 

     /// <summary> 
     /// Returns the wrapped object. 
     /// </summary> 
     public T Object 
     { 
      get 
      { 
       return ((T) m_oObject); 
      } 
     } 

     /// <summary> 
     /// Implicit cast to type T. 
     /// </summary> 
     /// <param name="oObject">Object to cast.</param> 
     /// <returns>Returns the ComPtr object cast to type T.</returns> 
     public static implicit operator T (ComPtr<T> oObject) 
     { 
      return (oObject.Object); 
     } 

     /// <summary> 
     /// Frees up resources. 
     /// </summary> 
     public void Dispose() 
     { 
      Dispose (true); 
      GC.SuppressFinalize (this); 
     } 

     /// <summary> 
     /// Frees up resurces used by the object. 
     /// </summary> 
     /// <param name="bDispose">When false, the function is called from the destructor.</param> 
     protected void Dispose (bool bDispose) 
     { 
      try 
      { 
       if (!m_bDisposeDone && (m_oObject != null)) 
       { 
        Marshal.ReleaseComObject (m_oObject); 
        m_oObject = null; 
       } 
      } 
      finally 
      { 
       m_bDisposeDone = true; 
      } 
     } 
    } 
} 
+0

Upvoted,很好的答案。儘管我認爲濫用「ComPtr 」這個名字有點多。 (和匈牙利符號) – 2012-03-25 03:02:09

+0

@RitchMelton感謝您的投票。你爲什麼認爲使用'ComPtr '是濫用這個名字? – xxbbcc 2012-03-25 03:58:07