Cerrar el proceso de aplicación de Excel en C # después del acceso a los datos

86

Estoy escribiendo una aplicación en C # que abre un archivo de plantilla de Excel para operaciones de lectura / escritura. Quiero que cuando el usuario cierre la aplicación, el proceso de aplicación de Excel se haya cerrado, sin guardar el archivo de Excel. Ver mi Administrador de tareas después de varias ejecuciones de la aplicación.

ingrese la descripción de la imagen aquí

Utilizo este código para abrir el archivo de Excel:

public Excel.Application excelApp = new Excel.Application();
public Excel.Workbook excelBook;
excelBook = excelApp.Workbooks.Add(@"C:/pape.xltx");

y para el acceso a los datos utilizo este código:

Excel.Worksheet excelSheet = (Worksheet)(excelBook.Worksheets[1]);
excelSheet.DisplayRightToLeft = true;
Range rng;
rng = excelSheet.get_Range("C2");
rng.Value2 = txtName.Text;

Veo preguntas similares en stackoverflow, como esta pregunta y esta , y pruebo las respuestas, pero no funciona.

Javad Yousefi
fuente
Excel y Word son muy lentos y vienen con un montón de peculiaridades como la que ha encontrado. Los archivos son archivos zip con algo de XML y otro contenido allí. También hay Open XML SDK (o quizás algo más reciente) que puede abrir los documentos. Dicho código también funciona sin Office instalado localmente. Considere no usar Excel
Sten Petrov

Respuestas:

95

Prueba esto:

excelBook.Close(0); 
excelApp.Quit();

Al cerrar el libro de trabajo, tiene tres parámetros opcionales:

Workbook.close SaveChanges, filename, routeworkbook 

Workbook.Close(false)o si está haciendo enlace tardío, a veces es más fácil usar cero. Workbook.Close(0) Así es como lo hice al automatizar el cierre de libros de trabajo.

También fui y busqué la documentación, y la encontré aquí: Libro de Excel Cerrar

Gracias,

Miguel
fuente
Gracias Estimado Michael, Funciona correctamente: excelBook.Close (0)
Javad Yousefi
Hola chicos, esto no funciona cuando abres un archivo de Excel para leerlo. A continuación se muestra el código var excelApp = new Application (); var workBooks = excelApp.Workbooks.Open @ "c: \ temp \ myexcel.xlsx"); workBooks.Close (0); excelApp.Quit ();
user1131926
He usado applicationClass ... y usé el código anterior para deshacerse de los objetos, pero no funciona ...
Singaravelan
3
Después de muchos intentos fallidos con todas estas respuestas, opté por esta solución para obtener el ID del proceso que abrí y eliminarlo directamente.
UndeadBob
@Singaravelan: ¿Ha comprobado la respuesta de David Clarke?
Thế Anh Nguyễn
22
xlBook.Save();
xlBook.Close(true);
xlApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);

prueba esto ... funcionó para mí ... deberías liberar ese objeto de aplicación xl para detener el proceso.

Sameera R.
fuente
14

Ref: https://stackoverflow.com/a/17367570/132599

Evite el uso de expresiones de llamada de doble punto, como esta:

var workbook = excel.Workbooks.Open(/*params*/)

... porque de esta manera crea objetos RCW no solo para el libro de trabajo, sino para los libros de trabajo, y debe liberarlo también (lo cual no es posible si no se mantiene una referencia al objeto).

Esto me resolvió el problema. Tu código se convierte en:

public Excel.Application excelApp = new Excel.Application();
public Excel.Workbooks workbooks;
public Excel.Workbook excelBook;
workbooks = excelApp.Workbooks;
excelBook = workbooks.Add(@"C:/pape.xltx");

...

Excel.Sheets sheets = excelBook.Worksheets;
Excel.Worksheet excelSheet = (Worksheet)(sheets[1]);
excelSheet.DisplayRightToLeft = true;
Range rng;
rng = excelSheet.get_Range("C2");
rng.Value2 = txtName.Text;

Y luego suelta todos esos objetos:

System.Runtime.InteropServices.Marshal.ReleaseComObject(rng);
System.Runtime.InteropServices.Marshal.ReleaseComObject(excelSheet);
System.Runtime.InteropServices.Marshal.ReleaseComObject(sheets);
excelBook .Save();
excelBook .Close(true);
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBook);
System.Runtime.InteropServices.Marshal.ReleaseComObject(workbooks);
excelApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);

Envuelvo esto en un try {} finally {}para asegurarme de que todo se libere incluso si algo sale mal (¿qué podría salir mal?)

public Excel.Application excelApp = null;
public Excel.Workbooks workbooks = null;
...
try
{
    excelApp = new Excel.Application();
    workbooks = excelApp.Workbooks;
    ...
}
finally
{
    ...
    if (workbooks != null) System.Runtime.InteropServices.Marshal.ReleaseComObject(workbooks);
    excelApp.Quit();
    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);
}
David Clarke
fuente
2
Esto me ha funcionado, asignando libros de trabajo a una variable para que pueda borrarse. También una observación, tenía exactamente el mismo código para borrar la aplicación de Excel en la aplicación web y la aplicación de consola. El código antes de la actualización para deshacerse del punto doble funcionó bien en la aplicación de la consola, pero cuando se ejecutó en la aplicación web no borró el EXCEL.EXE. No estoy seguro de por qué se comportaron de manera diferente, pero deshacerse de la referencia de doble punto lo solucionó en la aplicación web.
HierroHide
trabajando para mi. Creo que para liberar un objeto, debemos liberar todos los objetos relacionados. Tuve el mismo problema con el programa solidworks.
Gs.
1
Cierre el libro de trabajo, salga de la aplicación Excel, evite las llamadas de doble punto y libere todos los objetos COM creados. Eso funcionó para mí. Respuesta que salva vidas. Gracias.
Sinan ILYAS
13

Piense en esto, mata el proceso:

System.Diagnostics.Process[] process=System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (System.Diagnostics.Process p in process)
{
    if (!string.IsNullOrEmpty(p.ProcessName))
    {
        try
        {
            p.Kill();
        }
        catch { }
    }
}

Además, ¿intentaste cerrarlo normalmente?

myWorkbook.SaveAs(@"C:/pape.xltx", missing, missing, missing, missing, missing, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange, missing, missing, missing, missing, missing);
excelBook.Close(null, null, null);                 // close your workbook
excelApp.Quit();                                   // exit excel application
excel = null;                                      // set to NULL
Morris Miao
fuente
Gracias por su respuesta. Su primera solución cierra Todos los archivos de Excel abiertos. Cuando uso 'excelBook.Close', aparece el cuadro de diálogo Guardar en Excel y no lo quiero :(
Javad Yousefi
1
Lo edité y lo agregué con una línea para guardar como en el código que reemplazará el diálogo por código, espero que ayude. :)
Morris Miao
1
Acabo de usar esto con una verificación inicial de los procesos abiertos de Excel y guardé los ID en una lista. Luego hice un nuevo proceso de Excel, hice la edición que tenía que hacer y cerré cualquier proceso de Excel que no estuviera incluido en la lista de ID.
182764125216
No es la forma correcta de liberar Excel del administrador de tareas. Debe liberar todos los objetos que se crearon, incluidos los que se crearon detrás de escena, como WorkBOoks.
Ehh
De esta manera, mataría todos los procesos de Excel en ejecución. El problema es que puede haber otros usuarios o aplicaciones que utilicen Excel al mismo tiempo. Solo tenga en cuenta si está dispuesto a hacer esto.
Sinan ILYAS
5

Matar a Excel no siempre es fácil; vea este artículo: 50 formas de matar a Excel

Este artículo sigue los mejores consejos de Microsoft ( artículo de base de conocimientos de MS ) sobre cómo hacer que Excel se cierre correctamente, pero también se asegura de ello eliminando el proceso si es necesario. Me gusta tener un segundo paracaídas.

Asegúrese de cerrar todos los libros de trabajo abiertos, salir de la aplicación y liberar el objeto xlApp. Por último, compruebe si el proceso sigue activo y, de ser así, elimínelo.

Este artículo también se asegura de que no eliminemos todos los procesos de Excel, sino que solo elimine el proceso exacto que se inició.

Consulte también Obtener proceso de la manija de la ventana

Aquí está el código que uso: (funciona todo el tiempo)

Sub UsingExcel()

    'declare process; will be used later to attach the Excel process
    Dim XLProc As Process

    'call the sub that will do some work with Excel
    'calling Excel in a separate routine will ensure that it is 
    'out of scope when calling GC.Collect
    'this works better especially in debug mode
    DoOfficeWork(XLProc)

    'Do garbage collection to release the COM pointers
    'http://support.microsoft.com/kb/317109
    GC.Collect()
    GC.WaitForPendingFinalizers()

    'I prefer to have two parachutes when dealing with the Excel process
    'this is the last answer if garbage collection were to fail
    If Not XLProc Is Nothing AndAlso Not XLProc.HasExited Then
        XLProc.Kill()
    End If

End Sub

'http://msdn.microsoft.com/en-us/library/ms633522%28v=vs.85%29.aspx
<System.Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
    Private Shared Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, _
    ByRef lpdwProcessId As Integer) As Integer
End Function

Private Sub ExcelWork(ByRef XLProc As Process)

    'start the application using late binding
    Dim xlApp As Object = CreateObject("Excel.Application")

    'or use early binding
    'Dim xlApp As Microsoft.Office.Interop.Excel

    'get the window handle
    Dim xlHWND As Integer = xlApp.hwnd

    'this will have the process ID after call to GetWindowThreadProcessId
    Dim ProcIdXL As Integer = 0

    'get the process ID
    GetWindowThreadProcessId(xlHWND, ProcIdXL)

    'get the process
    XLProc = Process.GetProcessById(ProcIdXL)


    'do some work with Excel here using xlApp

    'be sure to save and close all workbooks when done

    'release all objects used (except xlApp) using NAR(x)


    'Quit Excel 
    xlApp.quit()

    'Release
    NAR(xlApp)

End Sub

Private Sub NAR(ByVal o As Object)
    'http://support.microsoft.com/kb/317109
    Try
        While (System.Runtime.InteropServices.Marshal.ReleaseComObject(o) > 0)
        End While
    Catch
    Finally
        o = Nothing
    End Try
End Sub
D_Bester
fuente
Esta fue la única solución que funcionó para mí. Gracias.
Jon Vote el
2

Me encontré con los mismos problemas y probé muchos métodos para resolverlo, pero no funciona. Finalmente, encontré el por mi camino. Alguna referencia ingrese la descripción del enlace aquí

Espero que mi código pueda ayudar a alguien en el futuro. He tardado más de dos días en solucionarlo. A continuación se muestra mi código:

//get current in useing excel
            Process[] excelProcsOld = Process.GetProcessesByName("EXCEL");
            Excel.Application myExcelApp = null;
            Excel.Workbooks excelWorkbookTemplate = null;
            Excel.Workbook excelWorkbook = null;
try{
    //DO sth using myExcelApp , excelWorkbookTemplate, excelWorkbook
}
catch (Exception ex ){
}
finally
            {
                //Compare the EXCEL ID and Kill it 
                Process[] excelProcsNew = Process.GetProcessesByName("EXCEL");
                foreach (Process procNew in excelProcsNew)
                {
                    int exist = 0;
                    foreach (Process procOld in excelProcsOld)
                    {
                        if (procNew.Id == procOld.Id)
                        {
                            exist++;
                        }
                    }
                    if (exist == 0)
                    {
                        procNew.Kill();
                    }        
                }
            }
Kenneth Wong
fuente
1

excelBook.Close (); excelApp.Quit (); agregue el final del código, podría ser suficiente. está funcionando en mi código

user2605046
fuente
Sí, pero cuando lo uso, aparece el cuadro de diálogo Guardar y no quiero que aparezca :(
Javad Yousefi
1

Puede matar el proceso con su propio COMobjeto excel pid

agregue en algún lugar debajo del código de importación dll

[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId);

y use

 if (excelApp != null)
            {
                int excelProcessId = -1;
                GetWindowThreadProcessId(new IntPtr(excelApp.Hwnd), ref excelProcessId);

                Process ExcelProc = Process.GetProcessById(excelProcessId);
                if (ExcelProc != null)
                {
                    ExcelProc.Kill();
                }
            }
Alican Kuklaci
fuente
1

He descubierto que es importante tener Marshal.ReleaseComObjectdentro de un Whilebucle Y terminar con Garbage Collection .

static void Main(string[] args)
{
    Excel.Application xApp = new Excel.Application();
    Excel.Workbooks xWbs = xApp.Workbooks;
    Excel.Workbook xWb = xWbs.Open("file.xlsx");

    Console.WriteLine(xWb.Sheets.Count);

    xWb.Close();
    xApp.Quit();

    while (Marshal.ReleaseComObject(xWb) != 0);
    while (Marshal.ReleaseComObject(xWbs) != 0);
    while (Marshal.ReleaseComObject(xApp) != 0);

    GC.Collect();
    GC.WaitForPendingFinalizers();
}
Emanuel Oliveira
fuente
0
         wb.Close();
         app.Quit();

         System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("Excel");
         foreach (System.Diagnostics.Process p in process)
         {
             if (!string.IsNullOrEmpty(p.ProcessName) && p.StartTime.AddSeconds(+10) > DateTime.Now)
             {
                 try
                 {
                     p.Kill();
                 }
                 catch { }
             }
         }

Cierra el proceso de los últimos 10 segundos con el nombre "Excel"

xrhstos
fuente
0

La forma correcta de cerrar todos los procesos de Excel

var _excel = new Application();
foreach (Workbook _workbook in _excel.Workbooks) {
    _workbook.Close(0);
}

_excel.Quit();
_excel = null;
var process = System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (var p in process) {
    if (!string.IsNullOrEmpty(p.ProcessName)) {
        try {
            p.Kill();
        } catch { }
    }
}
Md. Alim Ul Karim
fuente
0

Basado en otras soluciones. He usado esto:

IntPtr xAsIntPtr = new IntPtr(excelObj.Application.Hwnd);
excelObj.ActiveWorkbook.Close();

System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("Excel");
                foreach (System.Diagnostics.Process p in process)
                {
                    if (p.MainWindowHandle == xAsIntPtr)
                    {
                        try
                        {
                            p.Kill();
                        }
                        catch { }
                    }
                }

Usando el "MainWindowHandle" para identificar el proceso y cerrarlo.

excelObj: Este es mi interoperabilidad de aplicaciones excel objecto

merryrppin
fuente
0

Utilice una variable para cada objeto de Excel y debe realizar un ciclo Marshal.ReleaseComObject >0. Sin el ciclo, el proceso de Excel seguirá activo.

public class test{
        private dynamic ExcelObject;
        protected dynamic ExcelBook;
        protected dynamic ExcelBooks;
        protected dynamic ExcelSheet;

public void LoadExcel(string FileName)
        {
            Type t = Type.GetTypeFromProgID("Excel.Application");
            if (t == null) throw new Exception("Excel non installato");
            ExcelObject = System.Activator.CreateInstance(t);
            ExcelObject.Visible = false;
            ExcelObject.DisplayAlerts = false;
            ExcelObject.AskToUpdateLinks = false;
            ExcelBooks = ExcelObject.Workbooks;
            ExcelBook = ExcelBooks.Open(FileName,0,true);
            System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
            ExcelSheet = ExcelBook.Sheets[1];
        }
 private void ReleaseObj(object obj)
        {
            try
            {
                int i = 0;
             while(   System.Runtime.InteropServices.Marshal.ReleaseComObject(obj) > 0)
                {
                    i++;
                    if (i > 1000) break;
                }
                obj = null;
            }
            catch 
            {
                obj = null;
            }
            finally
            {
                GC.Collect();
            }
        }
        public void ChiudiExcel() {
            System.Threading.Thread.CurrentThread.CurrentCulture = ci;

            ReleaseObj(ExcelSheet);
            try { ExcelBook.Close(); } catch { }
            try { ExcelBooks.Close(); } catch { }
            ReleaseObj(ExcelBooks);
            try { ExcelObject.Quit(); } catch { }
            ReleaseObj(ExcelObject);
        }
}
usuario10170010
fuente
0

Podemos cerrar la aplicación de Excel mientras convertimos xls a xlsx usando el siguiente código. Cuando realizamos este tipo de tarea, la aplicación Excel se está ejecutando en el administrador de tareas, deberíamos cerrar este Excel que se ejecuta en segundo plano. Interop es un componente Com, para liberar el componente com usamos Marshal.FinalReleaseComObject.

 private void button1_Click(object sender, EventArgs e)
    {

        Excel03to07("D:\\TestExls\\TestExcelApp.XLS");

    }
    private void Excel03to07(string fileName)
    {
        string svfileName = Path.ChangeExtension(fileName, ".xlsx");
        object oMissing = Type.Missing;
        var app = new Microsoft.Office.Interop.Excel.Application();
        var wb = app.Workbooks.Open(fileName, oMissing, oMissing,
                        oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing);
        wb.SaveAs(svfileName, XlFileFormat.xlOpenXMLWorkbook, Type.Missing, Type.Missing, Type.Missing, Type.Missing, XlSaveAsAccessMode.xlNoChange, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

        wb.Close(false, Type.Missing, Type.Missing);
        app.Quit();
        GC.Collect();
        Marshal.FinalReleaseComObject(wb);
        Marshal.FinalReleaseComObject(app);
   }
Sumant Singh
fuente
0

La mayoría de los métodos funcionan, pero el proceso de Excel siempre permanece hasta que se cierra la aplicación.

Cuando mata el proceso de Excel una vez, no se puede ejecutar una vez más en el mismo hilo, no sé por qué.

Atanas Atanasov
fuente
-1
        GetWindowThreadProcessId((IntPtr)app.Hwnd, out iProcessId);
        wb.Close(true,Missing.Value,Missing.Value);
        app.Quit();
        System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("Excel");
        foreach (System.Diagnostics.Process p in process)
        {
            if (p.Id == iProcessId)
            {
                try
                {
                    p.Kill();
                }
                catch { }
            }
        }
}
[DllImport("user32.dll")]

private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

uint iProcessId = 0;

este GetWindowThreadProcessId encuentra el Id. de proceso correcto o sobresale .... Después lo mata .... Disfrútalo !!!

xrhstos
fuente
-1
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();
    }
}
Mehmet Yardım
fuente
Si bien este código puede resolver la pregunta, incluir una explicación de cómo y por qué esto resuelve el problema realmente ayudaría a mejorar la calidad de su publicación y probablemente resultaría en más votos a favor. Recuerde que está respondiendo la pregunta a los lectores en el futuro, no solo a la persona que pregunta ahora. Por favor, editar su respuesta para agregar explicación y dar una indicación de lo que se aplican limitaciones y supuestos.
Dave