2012-04-01 13 views
13

sto lavorando a un'applicazione win # C# visual studio 2008. l'app parla di file excel e sto usando Microsoft.Office.Interop.Excel; per farlo.Disporre in modo sicuro oggetti di interoperabilità di Excel in C#?

mi piacerebbe sapere come posso essere sicuro che gli oggetti vengano rilasciati anche quando c'è un errore?

ecco il mio codice:

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

come posso fare in modo che, anche se ottengo un errore dopo la cartella di lavoro aperto, che faccio in modo di disporre gli oggetti:

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

In altre parole, non importa quello che ho bisogno delle seguenti righe per eseguire

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

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

si prega di notare che ho provato questo come pure, con conseguente nello stesso numero

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

e ho fatto anche questo:

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

a questo punto non credo che sia possibile chiudere Excel da Visual Studio 2008. deve essere un bug o qualcosa del genere, ma ho provato i primi 20 siti web su questo e ottenendo lo stesso risultato: excel sta aprendo due istanze per qualche motivo e quando faccio la garbage collection ecc. (o meno) si chiude solo UN'istanza.

quando provo ad aprire il file, si dice che c'è un errore o è corrotto.

quando vado a task manager e uccidere il processo di Excel, il file verrà aperto senza problemi.]

c'è un modo per chiudere Excel con Visual Studio 2008? in tal caso, può fornirmi una guida o una soluzione per questo

+4

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

+0

puoi dirmi la differenza tra ciò che è succede con xlworkbook.close e xlapp.quit vs releaseObject? perché ho bisogno di entrambi? –

+1

[Questo] (http://stackoverflow.com/questions/158706/how-to-properly-clean-up-excel-interop-objects-in-c/159419#159419) risponde bene alla tua domanda. –

risposta

18

Per prima cosa presenterò uno releaseObject modificato e quindi fornirò un modello per utilizzarlo.

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

Ora, un modello da utilizzare:

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

Questo semplice approccio può essere esteso/nidificato sopra maggior parte delle situazioni. Io uso una classe wrapper personalizzata che implementa IDisposable per rendere più semplice questa attività.

+1

grazie per l'aiuto! sto ricevendo questo errore su tutte le chiamate releaseObject Errore Una proprietà o un indicizzatore non può essere passato come parametro out o ref –

+0

per favore nota sto usando .net 3.5 –

+0

I__ Sì, in C# 'ref' può essere usato con * variabili *. Nei miei progetti ho due metodi "releaseObject". Uno usa 'ref' (lo uso per tutte le * variabili *) e un altro non usa" ref "(lo uso raramente per alcune * proprietà *). Se "ref" è * non utilizzato *, basta sapere che non è possibile modificare il valore passato, nel chiamante. Per esempio. 'realeaseObject (ref variable)' o 'releaseObjectNonRef (Property); Proprietà = null; 'è richiesto. –

4

Verificare che non ci sono due problemi che stai vedendo nel codice:

  • che quando il programma si chiude Excel rimane come un processo in esecuzione
  • che quando si apre il file Excel vostro programma crea vedete un errore in Excel dice che il file è danneggiato o qualcosa del genere

ho copiato il gestore button1 click e il metodo di pst releaseObject nella tua domanda modificato in un VS2008 pulito, C# 3.5 applicazione Winform e fatto un paio di modifiche minori per eliminare entrambi i problemi che ho elencato sopra.

Per risolvere Excel non scaricare dalla memoria, chiamare releaseObject sull'oggetto range creato. Fatelo prima della vostra chiamata a releaseObject(xlWorkSheet); Ricordare tutti questi riferimenti è ciò che rende la programmazione COM Interop così divertente.

Per fissare il corrotto problema file di Excel aggiorna la chiamata WorkBook.SaveAs metodo per sostituire il secondo parametro (Excel.XlFileFormat.xlXMLSpreadsheet) con Type.Missing. Il metodo SaveAs gestirà questo correttamente per impostazione predefinita.

Sono sicuro che il codice che hai postato nella tua domanda è stato semplificato per aiutare a risolvere i problemi che stai riscontrando. È necessario utilizzare il blocco try..finally per dimostrare.

+0

grazie mille per questo !! Sto provando proprio ora. ma per favore puoi dirti che cosa stiamo usando xlXMLspreadsheet? non voglio che sia in formato xml –

+0

| _ Non sono sicuro di aver capito - Ho ottenuto le specifiche del formato xml dal tuo codice, dalla chiamata al metodo 'xlWorkBook.SaveAs'. Sto suggerendo di non farlo usando 'Type.Missing' al suo posto. –

+0

mi spiace volevo dire che volevo salvarlo esattamente nello stesso formato di –