2012-04-01 7 views
13

Ich arbeite an einer winforms C# Visual Studio 2008 Anwendung. Die App spricht mit Excel-Dateien und ich verwende Microsoft.Office.Interop.Excel;, um dies zu tun.Excel-Interop-Objekte in C# sicher entsorgen?

ich würde gerne wissen, wie kann ich sicherstellen, dass die objekte freigegeben werden, auch wenn es einen fehler gibt?

hier ist mein Code:

private void button1_Click(object sender, EventArgs e) 
{ 
    string myBigFile=""; 
    OpenFileDialog openFileDialog1 = new OpenFileDialog(); 
    DialogResult result = openFileDialog1.ShowDialog(); // Show the dialog. 
    if (result == DialogResult.OK) // Test result. 
     myBigFile=openFileDialog1.FileName; 

    Excel.Application xlApp; 
    Excel.Workbook xlWorkBook; 
    Excel.Worksheet xlWorkSheet; 
    Excel.Range range; 

    string str; 
    int rCnt = 0; 
    int cCnt = 0; 

    xlApp = new Excel.ApplicationClass(); 
    xlWorkBook = xlApp.Workbooks.Open(myBigFile, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", true, false, 0, true, 1, 0); 
    xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1); 

    range = xlWorkSheet.UsedRange; 

    /* 
    for (rCnt = 1; rCnt <= range.Rows.Count; rCnt++) 
    { 
     for (cCnt = 1; cCnt <= range.Columns.Count; cCnt++) 
     { 
      str = (string)(range.Cells[rCnt, cCnt] as Excel.Range).Value2; 
      MessageBox.Show(str); 
     } 
    } 
    */ 
    xlWorkSheet..EntireRow.Delete(Excel.XLDirection.xlUp) 

    xlWorkBook.SaveAs(xlWorkBook.Path + @"\XMLCopy.xls",   Excel.XlFileFormat.xlXMLSpreadsheet, Type.Missing, Type.Missing, 
    false, false, Excel.XlSaveAsAccessMode.xlNoChange, 
    Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); 

    xlWorkBook.Close(true, null, null); 
    xlApp.Quit(); 

    releaseObject(xlWorkSheet); 
    releaseObject(xlWorkBook); 
    releaseObject(xlApp); 
} 

private void releaseObject(object obj) 
{ 
    try 
    { 
     System.Runtime.InteropServices.Marshal.ReleaseComObject(obj); 
     obj = null; 
    } 
    catch (Exception ex) 
    { 
     obj = null; 
     MessageBox.Show("Unable to release the Object " + ex.ToString()); 
    } 
    finally 
    { 
     GC.Collect(); 
    } 
} 

, wie ich sicherstellen kann, dass selbst wenn ich einen Fehler nach dem Öffnen der Arbeitsmappe erhalten, dass ich sicher, der Gegenstände zu entsorgen:

Excel.Application xlApp; 
Excel.Workbook xlWorkBook; 
Excel.Worksheet xlWorkSheet; 
Excel.Range range; 

In Mit anderen Worten, egal, was ich brauche die folgenden Zeilen zu laufen

Bitte beachten Sie, dass ich dies versucht habe Auch in der gleichen Ausgabe resultierende

xlWorkBook.Close(false, System.Reflection.Missing.Value, System.Reflection.Missing.Value); 


       xlApp.Quit(); 

       Marshal.ReleaseComObject(xlWorkSheet); 
       Marshal.ReleaseComObject(xlWorkBook); 
       Marshal.ReleaseComObject(xlApp); 

       xlWorkSheet = null; 
       xlWorkBook = null; 
       xlApp = null; 

       GC.GetTotalMemory(false); 
       GC.Collect(); 
       GC.WaitForPendingFinalizers(); 
       GC.Collect(); 
       GC.GetTotalMemory(true); 

und ich tat dies auch:

GC.Collect()     ; 
       GC.WaitForPendingFinalizers(); 
       GC.Collect()     ; 
       GC.WaitForPendingFinalizers(); 

       Marshal.FinalReleaseComObject(xlWorkSheet); 

       xlWorkBook.Close(Type.Missing, Type.Missing, Type.Missing); 
       Marshal.FinalReleaseComObject(xlWorkBook); 

       xlApp.Quit(); 
       Marshal.FinalReleaseComObject(xlApp); 

an dieser Stelle Ich glaube nicht, es ist möglich, von Visual Studio zu schließen Excel 2008 es sein muss, Fehler oder etwas, aber ich habe versucht, die Top-20-Websites auf diesem und das gleiche Ergebnis: Excel öffnet zwei Instanzen aus irgendeinem Grund und wenn ich die Garbage Collection usw. tun. (oder nicht) es schließt nur eine Instanz.

Wenn ich versuche, die Datei zu öffnen, wird ein Fehler angezeigt oder sie ist beschädigt.

, wenn ich auf dem Task-Manager gehen und den Excel-Prozess beenden, wird die Datei ohne Probleme zu öffnen.]

ist es eine Möglichkeit, mit Visual Studio 2008 übertreffen zu schließen? Wenn ja, können Sie mir bitte eine Anleitung oder eine Lösung zu diesem

+4

Rufen Sie nicht GC.Collection() ... http://blogs.msdn.com/b/ricom/archive/2004/11/29/271829.aspx –

+0

können Sie mir bitte sagen, der Unterschied zwischen was ist passiert mit xlworkbook.close und xlapp.quit vs ReleaseObject? Warum brauche ich beides? –

+1

[This] (http://stackoverflow.com/questions/158706/how-to-properly-clean-up-excel-interop-objects-in-c/159419#159419) beantwortet Ihre Frage schön. –

Antwort

18

zuerst Ich werde eine modifizierte releaseObject vorstellen, und dann werde ich ein Muster zur Verfügung stellen, es zu verwenden.

using Marshal = System.Runtime.InteropServices.Marshal; 
private void releaseObject(ref object obj) // note ref! 
{ 
    // Do not catch an exception from this. 
    // You may want to remove these guards depending on 
    // what you think the semantics should be. 
    if (obj != null && Marshal.IsComObject(obj)) { 
     Marshal.ReleaseComObject(obj); 
    } 
    // Since passed "by ref" this assingment will be useful 
    // (It was not useful in the original, and neither was the 
    // GC.Collect.) 
    obj = null; 
} 

nun ein Muster zu verwenden:

private void button1_Click(object sender, EventArgs e) 
{ 
    // Declare. Assign a value to avoid a compiler error. 
    Excel.Application xlApp = null; 
    Excel.Workbook xlWorkBook = null; 
    Excel.Worksheet xlWorkSheet = null; 

    try { 
     // Initialize 
     xlApp = new Excel.ApplicationClass(); 
     xlWorkBook = xlApp.Workbooks.Open(myBigFile, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", true, false, 0, true, 1, 0); 
     // If the cast fails this like could "leak" a COM RCW 
     // Since this "should never happen" I wouldn't worry about it. 
     xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1); 
     ... 
    } finally { 
     // Release all COM RCWs. 
     // The "releaseObject" will just "do nothing" if null is passed, 
     // so no need to check to find out which need to be released. 
     // The "finally" is run in all cases, even if there was an exception 
     // in the "try". 
     // Note: passing "by ref" so afterwords "xlWorkSheet" will 
     // evaluate to null. See "releaseObject". 
     releaseObject(ref xlWorkSheet); 
     releaseObject(ref xlWorkBook); 
     // The Quit is done in the finally because we always 
     // want to quit. It is no different than releasing RCWs. 
     if (xlApp != null) { 
      xlApp.Quit(); 
     } 
     releaseObject(ref xlApp);  
    } 
} 

Dieser einfache Ansatz erweitert werden kann/verschachtelte über die meisten Situationen. Ich benutze eine benutzerdefinierte Wrapper-Klasse, die IDisposable implementiert, um diese Aufgabe zu erleichtern.

+1

vielen dank für die hilfe! Ich erhalte diesen Fehler bei allen releaseObject-Aufrufen Fehler Eine Eigenschaft oder ein Indexer darf nicht als out- oder ref-Parameter übergeben werden –

+0

Bitte beachten Sie, dass ich .net 3.5 verwenden –

+0

I__ Ja, in C# 'ref' kann nur verwendet werden mit * Variablen *. In meinen Projekten habe ich zwei "releaseObject" -Methoden. Man verwendet "ref" (ich verwende es für alle * Variablen *) und ein anderes verwendet nicht "ref" (ich verwende es selten für einige * Eigenschaften *). Wenn "ref" * nicht verwendet wird *, dann wissen Sie einfach, dass Sie den übergebenen Wert im Caller nicht ändern können. Z.B. 'realeaseObject (ref-Variable)' oder 'releaseObjectNonRef (Property); Property = null; 'ist erforderlich. –

4

Stellen Sie sicher, dass es zwei Probleme, die Sie in Ihrem Code sind zu sehen:

  • dass, wenn das Programm beendet Excel bleibt als laufenden Prozess
  • dass, wenn Sie die Excel-Datei Ihr Programm öffnen erstellt Sie sehen ein Fehler in Excel dass die Datei beschädigt ist oder so

ich kopierte den button1 Click-Handler und releaseObject Methode des pst in Ihrer editierten Frage in eine saubere VS2008, C# 3.5 Winform Anwendung und ein paar kleinere Änderungen vorgenommen, um beide oben genannten Probleme zu beseitigen.

Um zu beheben, dass Excel nicht aus dem Speicher entladen wird, rufen Sie releaseObject auf dem range Objekt auf, das Sie erstellt haben. Tun Sie dies vor Ihrem Anruf an releaseObject(xlWorkSheet); Erinnern Sie sich an alle diese Referenzen ist, was COM Interop Programmierung so viel Spaß macht.

Um das beschädigte Excel-Dateiproblem zu beheben, aktualisieren Sie Ihren Methodenaufruf , um den zweiten Parameter (Excel.XlFileFormat.xlXMLSpreadsheet) durch Type.Missing zu ersetzen. Die SaveAs Methode wird dies standardmäßig korrekt behandeln.

Ich bin sicher, dass der Code, den Sie in Ihrer Frage gepostet haben, vereinfacht wird, um die Probleme zu beheben, die Sie haben. Sie sollten den try..finally block pst demonstrieren.

+0

Vielen Dank für fth this !! Ich versuche es gerade jetzt. aber bitte kannst du ihnen sagen, warum wir xlXMLspreadsheet benutzen? Ich möchte nicht, dass es xml-Format ist –

+0

| _ Nicht sicher, ich verstehe - ich habe die XML-Format-Spezifikation aus Ihrem Code, aus der 'xlWorkBook.SaveAs' Methodenaufruf. Ich schlage vor, dies nicht zu tun, indem ich 'Type.Missing' an seiner Stelle benutze. –

+0

Es tut mir leid, ich meinte, dass ich es genau das gleiche Format speichern wollte wie –