none
Controlar procesos RRS feed

  • Pregunta

  • Hola, buenos días a todos.

    Tengo una app que crea ciertos reportes en archivos Excels y me gustaría controlarlos a la hora de cerrarlos.

    Cuando hago esto

    Excel.Application xlApp = new Excel.Application();

    se crea un proceso de Excel, el tema es que necesito, de alguna forma, obtener el Process ID de ese proceso Excel para luego más tarde cerrarlo. 

    Para cerrarlo hago esto

    foreach (System.Diagnostics.Process proceso in System.Diagnostics.Process.GetProcesses())
    {
        if (proceso.ProcessName == "EXCEL" || proceso.ProcessName == "excel")
        {
            proceso.Kill();
        }
    }

    pero el problema es que me cierra todos los procesos de excel, lo que necesito es cerrar uno en particular por su ID...

    Cómo puedo obtener el Process ID de una aplicación abierta por el programa actual??? Hay alguna forma de poder lograr esto?

    Muchas gracias a todos.

    Saludos

    martes, 3 de diciembre de 2019 12:37

Respuestas

  • Gracias por tu respuesta, mira, justamente tengo esto 

     private void CerrarObjeto(object obj)
            {
                try
                {
                    //LIBERA TODOS LOS OBJETOS Y RECURSOS DEL EXCEL
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
                    obj = null;
                }
                catch (Exception)
                {
                    obj = null;
                }
                finally
                {
                    GC.Collect();
                }
            }

    pero no está funcionando, los procesos Excel siguen ejecutándose en el pc, por eso me gustaría forzar el cierre con un Kill pero al proceso en particular.

    Saludos y gracias

    martes, 3 de diciembre de 2019 13:25
  • Estimados, lo resolví de la siguiente manera.

    [DllImport("user32.dll", SetLastError = true)]
    private static extern int GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId);
    
    
    if (xlApp != null)
    {
        int excelProcessId = -1;
        GetWindowThreadProcessId(new IntPtr(xlApp.Hwnd), ref excelProcessId);
    
        System.Diagnostics.Process ExcelProc = System.Diagnostics.Process.GetProcessById(excelProcessId);
        if (ExcelProc != null)
        {
            ExcelProc.Kill();
        }
    }

    Quizá no es la mejor forma y debería ser con ReleaseCOM pero funciona.

    Muchas gracias.

    Saludos.

    martes, 3 de diciembre de 2019 20:00

Todas las respuestas

  • No es el enfoque correcto. Lo adecuado es programar bien tu aplicacion de manera que cierre correctamente todos los objetos COM que cree mediante COM/Interop contra Excel. Si los destruyes correctamente, entonces el "reference counting" de COM detecta cuando el contador de usos llega a cero, y entonces cierra automaticamente el servidor COM (es decir, el Excel en este caso). Si esto se hace bien hecho, nunca se necesita el "Kill".

    Esencialmente, lo que tienes que recordar es que hay que llamar a Marshall.ReleaseComObject por cada uno de tus objetos cuando termines de utilizarlo:

    Worksheet hoja = xlApp.Worksheets.Open(...);
    ...
    Marshal.ReleaseComObject(hoja);

    Esto tienes que repetirlo por cada uno de los objetos COM que crees en tu programa a partir de Excel (incluyendo el original xlApp). Si lo haces bien, sin olvidar ni un solo objeto, el Excel se cierra automaticamente en cuanto ejecutas el ReleaseComObject del ultimo de tus objetos. Haz una busqueda en Internet y encontraras muchisima informacion sobre este tema, ya que es algo frecuentisimo y super-conocido.

    martes, 3 de diciembre de 2019 13:11
    Moderador
  • Gracias por tu respuesta, mira, justamente tengo esto 

     private void CerrarObjeto(object obj)
            {
                try
                {
                    //LIBERA TODOS LOS OBJETOS Y RECURSOS DEL EXCEL
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
                    obj = null;
                }
                catch (Exception)
                {
                    obj = null;
                }
                finally
                {
                    GC.Collect();
                }
            }

    pero no está funcionando, los procesos Excel siguen ejecutándose en el pc, por eso me gustaría forzar el cierre con un Kill pero al proceso en particular.

    Saludos y gracias

    martes, 3 de diciembre de 2019 13:25
  •  justamente tengo esto  [...] pero no está funcionando

    Cuando no funciona, típicamente es porque se ha pasado por alto algún objeto sobre el que no se está llamando a CerrarObjeto. A veces no es evidente, sobre todo si escribes una expresión compleja que accede p.ej. a una celda de un rango de una hoja. Después de hacerlo, hay que liberar la celda, el rango y la hoja. No basta con hacer el "release" de la hoja pensando que eso libera todos los objetos dependientes de esa hoja.

    El problema del kill es que no se sabe cuál es el Excel que está atendiendo tus peticiones COM. De hecho, es posible que un mismo servidor COM atienda peticiones de varios clientes COM, y por eso lleva el "conteo de conexiones" para desconectarse al cerrar la última. Si lo "mata" uno de los consumidores COM, puede "fastidiar" a los otros consumidores que estuviesen usando el objeto en ese momento.

    martes, 3 de diciembre de 2019 17:03
    Moderador
  • Estimados, lo resolví de la siguiente manera.

    [DllImport("user32.dll", SetLastError = true)]
    private static extern int GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId);
    
    
    if (xlApp != null)
    {
        int excelProcessId = -1;
        GetWindowThreadProcessId(new IntPtr(xlApp.Hwnd), ref excelProcessId);
    
        System.Diagnostics.Process ExcelProc = System.Diagnostics.Process.GetProcessById(excelProcessId);
        if (ExcelProc != null)
        {
            ExcelProc.Kill();
        }
    }

    Quizá no es la mejor forma y debería ser con ReleaseCOM pero funciona.

    Muchas gracias.

    Saludos.

    martes, 3 de diciembre de 2019 20:00