2012-01-23 6 views
6

Mi sto imbattendo in un problema con un po 'di codice che sto eseguendo il debug. L'interoperabilità di Excel viene utilizzata per estrarre alcuni valori da una cartella di lavoro; tuttavia, Excel rimane aperto dopo che il programma è stato chiuso. Ho provato la soluzione tradizionale, ma conserva ancora un riferimento a Excel aperto su tutte le macchine in cui il codice viene eseguitoIl processo di Excel rimane aperto dopo l'interruzione; metodo tradizionale non funzionante

private void TestExcel() 
    { 
     Excel.Application excel = new Excel.Application(); 
     Excel.Workbooks books = excel.Workbooks; 
     Excel.Workbook book = books.Open("C:\\test.xlsm"); 

     book.Close(); 
     books.Close(); 
     excel.Quit(); 

     Marshal.ReleaseComObject(book); 
     Marshal.ReleaseComObject(books); 
     Marshal.ReleaseComObject(excel); 
    } 

Anche questo semplice pezzo di codice mantiene il processo in esecuzione con più file (xlsm, xlsx, xls). In questo momento abbiamo una soluzione per uccidere i processi di Excel che abbiamo aperto, ma preferirei farlo funzionare per la mia sanità mentale.

Devo aggiungere che lo ho ristretto alla variabile Workbook. Se rimuovo la chiamata a books.Open() e tutti i riferimenti a book, si chiude correttamente.

+0

Il tuo codice ha funzionato quando l'ho testato, potresti ottenere un'eccezione in fase di esecuzione che causa il problema? – msmucker0527

risposta

11

Questo ha lavorato con successo per me:

 xlApp.Quit(); 

     //release all memory - stop EXCEL.exe from hanging around. 
     if (xlWorkBook != null) { Marshal.ReleaseComObject(xlWorkBook); } //release each workbook like this 
     if (xlWorkSheet != null) { Marshal.ReleaseComObject(xlWorkSheet); } //release each worksheet like this 
     if (xlApp != null) { Marshal.ReleaseComObject(xlApp); } //release the Excel application 
     xlWorkBook = null; //set each memory reference to null. 
     xlWorkSheet = null; 
     xlApp = null; 
     GC.Collect(); 
+2

Contrassegnato come risposta perché è corretto. Anche se non funziona ancora sulla mia macchina dev, funziona su un'installazione pulita o sui miei computer di casa. Dispari. – bradenb

3

Sono un amatore di COM totale, l'ho usato per una cosa minore in un progetto molto tempo fa, ma qui c'è un frammento che ho usato lì. Probabilmente l'ho trovato da qualche parte online, non ricordo. In ogni caso, mi si incolla il suo splendore;)

public static class ComBlackBox 
{ 
    public static void ReleaseObject(object obj) 
    { 
     try 
     { 
      System.Runtime.InteropServices.Marshal.ReleaseComObject(obj); 
      obj = null; 
     } 
     catch (ArgumentException ex) 
     { 
      obj = null; 
      MessageBox.Show("Unable to release the Object " + ex.Message); 
     } 
     finally 
     { 
      GC.Collect(); 
     } 
    } 
} 

Sono in grado di provarlo ora, ma probabilmente funzionato (sinceramente non ricordo i dettagli). Forse ti aiuterà. Sentitevi liberi di segnalare eventuali problemi evidenti con questo codice, sono davvero ben lungi dall'essere COM-letterato;)

+1

Questo lavoro è parzialmente dovuto al fatto che GC.Collect() alla fine prova a catturare alla fine. Tuttavia, in realtà non chiude eccellentemente fino a quando non arriva alla parte finale. Perché ? perché l'oggetto è passato per valore, se si passa il foglio/fogli/cartella di lavoro o Excel nella funzione. I loro valori COM rimarranno gli stessi, indipendentemente da ciò che si fa con l'oggetto stesso. A meno che non sia possibile passare questi oggetti COM per riferimento, ma l'ho provato, e non me lo ha permesso. –

2

Questo è come ho ottenuto questo problema:

// Store the Excel processes before opening. 
Process[] processesBefore = Process.GetProcessesByName("excel"); 

// Open the file in Excel. 
Application excelApplication = new Application(); 
Workbook excelWorkbook = excelApplication.Workbooks.Open(Filename); 

// Get Excel processes after opening the file. 
Process[] processesAfter = Process.GetProcessesByName("excel"); 

// Now find the process id that was created, and store it. 
int processID = 0; 
foreach (Process process in processesAfter) 
{ 
    if (!processesBefore.Select(p => p.Id).Contains(process.Id)) 
    { 
     processID = process.Id; 
    } 
} 

// Do the Excel stuff 

// Now close the file with the COM object. 
excelWorkbook.Close(); 
excelApplication.Workbooks.Close(); 
excelApplication.Quit(); 

// And now kill the process. 
if (processID != 0) 
{ 
    Process process = Process.GetProcessById(processID); 
    process.Kill(); 
} 
0

Questo codice funziona per me.

//Declare separate object variables 
Excel.Application xlApp = new Excel.Application(); 
Excel.Workbooks xlWorkbooks = xlApp.Workbooks; 
Excel.Workbook xlWorkbook = xlWorkbooks.Add(Missing.Value); 
Excel.Worksheet xlWorksheet = (Excel.Worksheet)xlWorkbook.Worksheets.get_Item(1); 

//Create worksheet 

xlWorkbook.Close(false, Missing.Value, Missing.Value); 
xlWorkbooks.Close(); 
xlApp.Quit(); 

Marshal.FinalReleaseComObject(xlWorksheet); 
Marshal.FinalReleaseComObject(xlWorkbook); 
Marshal.FinalReleaseComObject(xlWorkbooks); 
Marshal.FinalReleaseComObject(xlApp); 

xlWorksheet = null; 
xlWorkbook = null; 
xlWorkbooks = null; 
xlApp = null; 

GC.Collect(); 

This article from Microsoft ha alcune buone informazioni su questo problema.

Problemi correlati