2012-01-23 11 views
6

Tengo un problema con un código que estoy depurando. La interoperabilidad de Excel se usa para extraer algunos valores de un libro de trabajo; sin embargo, Excel permanece abierto después de que el programa haya salido. He intentado la solución tradicional, pero todavía mantiene una referencia a Excel abierto en todos los equipos donde se ejecuta el códigoEl proceso de Excel permanece abierto después de la interoperabilidad; El método tradicional no funciona

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

Incluso esta simple trozo de código mantiene el proceso en ejecución con múltiples archivos (xlsm, xlsx, xls). En este momento tenemos una solución para eliminar los procesos de Excel que hemos abierto, pero prefiero que esto funcione por mi propia cordura.

Debo añadir que lo tengo reducido a la variable Workbook. Si elimino la llamada a books.Open() y todas las referencias a book, se cierra con éxito.

+0

Tu código funcionó cuando lo probé, ¿podrías estar obteniendo una excepción en el momento de la ejecución que causa el problema? – msmucker0527

Respuesta

11

Esto ha funcionado con éxito para mí:

 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

Marcado como respuesta porque es correcto. Aunque todavía no funciona en mi máquina de desarrollo, esto funciona en una instalación limpia o en las computadoras de mi casa. Impar. – bradenb

3

Soy un aficionado de COM total, lo usé por una cosa menor en un proyecto hace mucho tiempo, pero aquí hay un fragmento que utilicé allí. Probablemente lo encontré en algún lugar en línea, no recuerdo. En cualquier caso, me pego su gloria;)

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

Soy incapaz de probarlo ahora, pero es probable que trabajé (Sinceramente, no recuerdo ningún detalle). Tal vez te ayude. No dude en señalar cualquier problema obvio con este código, realmente estoy lejos de ser COM-leer y escribir;)

+1

Esto funciona en parte porque tiene GC.Collect() en el último intento de captura al final. Sin embargo, realmente no se destaca hasta que llega a la parte final. Por qué ? porque el objeto se pasa por valor, si pasa la hoja/hojas/libro de trabajo o sobresale en la función. Sus valores COM permanecerán iguales, no importa lo que hagas con el objeto en sí. A menos que pueda pasar estos objetos COM por referencia, pero lo intenté y no me lo permitió. –

2

Esto es cómo llegué a este 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

Este código funciona para mí.

//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 tiene alguna buena información sobre este tema.

Cuestiones relacionadas