2012-03-24 2 views
0

Lets sagen, dass ich eine Komponente habe, die etwas mit Workbook-Objekt und irgendwo in der Mitte dieser Methode Körper habe ich Aufruf zu einer Methode einer anderen Klasse. Zum Beispiel:COM-Objekt Excel Interop aufräumen

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); 
    } 
} 

Und in diesem otherComponent.MethodCall (Dokument); Ich benutze ein Dokument und durchlaufe es durch seine Arbeitsblätter.

EDIT zu konkreter auf Frage. Soll ich ReleaseCOMObject für ein Dokument und für Worksheets in otherComponent.MethodCall (Dokument) aufrufen oder nicht?

Ich hatte nie wirklich eine gute Erklärung, wie ich diesen nicht verwalteten Code verwalten soll. Ich würde mich sehr freuen, wenn mir jemand das erklären könnte.

+0

Im Allgemeinen sollte die Methode, die ein Objekt erstellt hat, für die Bereinigung zuständig sein. Es ist etwas vage, was in diesem Szenario aus "Aufräumen" besteht. Sie sollten den Initialisierungs- und Bereinigungscode sowie einige weitere Erklärungen zu dem, was Sie tun, "die später Probleme verursachen könnten", veröffentlichen. –

Antwort

4

Sie müssen alle lokalen Objekte in dem Bereich, in dem Sie sie erstellen, manuell freigeben. Wenn Sie Office-Anwendungen über die Automatisierung verwenden, verlassen Sie sich nicht auf den Garbage Collector, um diese Objekte zu bereinigen. Selbst wenn dies richtig ist, kann es einige Zeit dauern, bis sie sich einschaltet und Sie möglicherweise temporäre Objekte mit Referenzen auf andere Objekte erhalten dass du denkst, dass sie schon weg sind.

This is a somewhat related question mit mehr Details, die möglicherweise für Sie gelten, wenn Sie versuchen, Excel von Ihrer Anwendung mit Excel zu starten, die ausgeblendet wird.

Der Teil, den Sie auf jeden Fall relevant ist, ist dies:

  • Wrap jede einzelne Funktion, die Excel in einem try..catch Block verwendet eine mögliche Ausnahme zu erfassen.
  • Geben Sie alle Excel-Objekte immer explizit frei, indem Sie Marshal.ReleaseComObject() aufrufen und dann Ihre Variablen auf null setzen, sobald Sie sie nicht benötigen. Geben Sie diese Objekte immer in einem finally-Block frei, um sicherzustellen, dass ein fehlgeschlagener Excel-Methodenaufruf nicht zu einem dangling COM-Objekt führt.
  • Wenn ein Fehler auftritt, schließen Sie die von Ihnen verwendete Excel-Instanz. Es ist nicht wahrscheinlich, dass Sie von Excel-bezogenen Fehlern wiederherstellen können und je länger Sie die Instanz behalten, desto länger werden Ressourcen verbraucht.
  • Wenn Sie Excel beenden, stellen Sie sicher, dass Sie diesen Code vor rekursiven Aufrufen schützen. Wenn Ihre Ausnahmehandler versuchen, Excel herunterzufahren, während Ihr Code bereits Excel herunterfährt, wird am Ende ein toter Excel angezeigt Beispiel.
  • Rufen Sie GC.Collect() und GC.WaitForPendingFinalizers() direkt nach Aufruf der Application.Quit()-Methode auf, um sicherzustellen, dass das .NET Framework alle Excel-COM-Objekte sofort freigibt.

bearbeiten: das ist, nachdem Sie mehr Details zu Ihrer Frage hinzugefügt.

In otherComponent Sie nicht über die Workbook und Document Objekte freizugeben müssen. Diese beiden Objekte werden in Ihrem ersten Objekt erstellt, was bedeutet, dass das erste Objekt der Eigentümer ist. Da es das erste Objekt ist, das Ihre Top-Level-Excel-Objekte besitzt (vorausgesetzt, Sie haben auch ein Application Objekt irgendwo), kann Ihr erstes Objekt otherComponent aufrufen, Workbook und Document übergeben und dann bei der Rückkehr, bereinigen sie.Wenn Sie keines dieser Objekte in Ihrer MainComponent verwenden, sollten Sie die Excel-bezogenen Objekte innerhalb otherComponent erstellen und sie dort aufräumen.

Mit COM-Interop sollten Sie Ihre COM-Objekte so nahe an dem Ort, wo Sie sie benötigen, erstellen und sie so schnell wie möglich freigeben. Dies gilt insbesondere für Office-Anwendungen.

Ich machte diese Klasse, um COM-Objekte einfacher zu machen: dieser Wrapper ist Einweg, so können Sie using(...) mit Ihren COM-Objekten verwenden - wenn der Bereich using vorbei ist, gibt der Wrapper das COM-Objekt.

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, gute Antwort. Obwohl ich denke, den Namen ComPtr zu missbrauchen ist ein bisschen viel. (Und die ungarische Schreibweise) –

+0

@RitchMelton Danke für die Abstimmung. Warum denkst du mit 'ComPtr ' missbrauchen den Namen? – xxbbcc