Estoy usando la interoperabilidad de Excel en C # ( ApplicationClass
) y he colocado el siguiente código en mi cláusula finalmente:
while (System.Runtime.InteropServices.Marshal.ReleaseComObject(excelSheet) != 0) { }
excelSheet = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Aunque este tipo de trabajo funciona, el Excel.exe
proceso todavía está en segundo plano incluso después de cerrar Excel. Solo se lanza una vez que mi aplicación se cierra manualmente.
¿Qué estoy haciendo mal o hay una alternativa para garantizar que los objetos de interoperabilidad se eliminen correctamente?
c#
excel
interop
com-interop
Infierno
fuente
fuente
Respuestas:
Excel no se cierra porque su aplicación aún contiene referencias a objetos COM.
Supongo que estás invocando al menos un miembro de un objeto COM sin asignarlo a una variable.
Para mí fue el objeto excelApp.Worksheets que utilicé directamente sin asignarlo a una variable:
No sabía que internamente C # creó un contenedor para el objeto COM de Hojas de trabajo que no fue liberado por mi código (porque no lo sabía) y fue la razón por la cual Excel no se descargó.
Encontré la solución a mi problema en esta página , que también tiene una buena regla para el uso de objetos COM en C #:
Entonces, con este conocimiento, la forma correcta de hacer lo anterior es:
ACTUALIZACIÓN POSTERIOR A MORTEM:
Quiero que todos los lectores lean esta respuesta de Hans Passant con mucho cuidado, ya que explica la trampa en la que yo y muchos otros desarrolladores nos encontramos. Cuando escribí esta respuesta hace años, no sabía sobre el efecto que tiene el depurador para el recolector de basura y saqué las conclusiones equivocadas. Mantengo mi respuesta inalterada por el bien de la historia, pero lea este enlace y no siga el camino de "los dos puntos": comprender la recolección de basura en .NET y limpiar objetos de interoperabilidad de Excel con IDisposable
fuente
En realidad, puede liberar su objeto de aplicación de Excel de manera limpia, pero debe tener cuidado.
El consejo de mantener una referencia con nombre para absolutamente cada objeto COM al que acceda y luego liberarlo explícitamente
Marshal.FinalReleaseComObject()
es correcto en teoría, pero, desafortunadamente, es muy difícil de administrar en la práctica. Si alguna vez se desliza a algún lado y usa "dos puntos", o itera celdas a través de unfor each
bucle, o cualquier otro tipo de comando similar, entonces tendrá objetos COM sin referencia y se arriesgará a bloquearse. En este caso, no habría forma de encontrar la causa en el código; tendría que revisar todo su código a simple vista y, con suerte, encontrar la causa, una tarea que podría ser casi imposible para un gran proyecto.La buena noticia es que no tiene que mantener una referencia de variable con nombre para cada objeto COM que use. En su lugar, llama
GC.Collect()
y luegoGC.WaitForPendingFinalizers()
libera todos los objetos (generalmente menores) a los que no tienes una referencia, y luego libera explícitamente los objetos a los que sí tienes una referencia de variable con nombre.También debe publicar sus referencias nombradas en orden inverso de importancia: primero los objetos de rango, luego las hojas de trabajo, los libros de trabajo y, finalmente, su objeto de aplicación de Excel.
Por ejemplo, suponiendo que tuviera una variable de objeto Range llamada
xlRng
, una variable de hoja de trabajo llamadaxlSheet
, una variable dexlBook
libro de trabajo llamada y una variable de aplicación de Excel llamadaxlApp
, entonces su código de limpieza podría tener el siguiente aspecto:En la mayoría de los ejemplos de código que verá para limpiar objetos COM de .NET, las llamadas
GC.Collect()
yGC.WaitForPendingFinalizers()
se realizan DOS VECES como en:Sin embargo, esto no debería ser necesario, a menos que esté utilizando Visual Studio Tools para Office (VSTO), que utiliza finalizadores que hacen que se promueva un gráfico completo de objetos en la cola de finalización. Dichos objetos no se liberarán hasta la próxima recolección de basura. Sin embargo, si no está usando VSTO, debería poder llamar
GC.Collect()
yGC.WaitForPendingFinalizers()
solo una vez.Sé que llamar explícitamente
GC.Collect()
es un no-no (y ciertamente hacerlo dos veces suena muy doloroso), pero para ser sincero, no hay forma de evitarlo. A través de las operaciones normales, generará objetos ocultos a los que no tiene referencias que, por lo tanto, no puede liberar por ningún otro medio que no sea llamarGC.Collect()
.Este es un tema complejo, pero esto es todo lo que hay que hacer. Una vez que establezca esta plantilla para su procedimiento de limpieza, puede codificar normalmente, sin necesidad de envoltorios, etc.
Tengo un tutorial sobre esto aquí:
Automatización de programas de Office con VB.Net / COM Interop
Está escrito para VB.NET, pero no se desanime por eso, los principios son exactamente los mismos que cuando se usa C #.
fuente
Prefacio: mi respuesta contiene dos soluciones, así que tenga cuidado al leer y no se pierda nada.
Hay diferentes formas y consejos sobre cómo hacer que la instancia de Excel se descargue, como:
Liberando CUALQUIER objeto com explícitamente con Marshal.FinalReleaseComObject () (sin olvidar los objetos com creados implícitamente). Para liberar cada objeto com creado, puede usar la regla de los 2 puntos mencionados aquí:
¿Cómo limpio correctamente los objetos de interoperabilidad de Excel?
Llamar a GC.Collect () y GC.WaitForPendingFinalizers () para hacer que CLR libere objetos com no utilizados * (En realidad, funciona, vea mi segunda solución para más detalles)
Verificando si la aplicación com-server puede mostrar un cuadro de mensaje esperando a que el usuario responda (aunque no estoy seguro de que pueda evitar que Excel se cierre, pero escuché sobre ello varias veces)
Enviar mensaje WM_CLOSE a la ventana principal de Excel
Ejecutando la función que funciona con Excel en un dominio de aplicación separado. Algunas personas creen que la instancia de Excel se cerrará cuando se descargue AppDomain.
Matar todas las instancias de Excel que se instanciaron después de que comenzara nuestro código de interoperabilidad de Excel.
¡PERO! ¡Algunas veces todas estas opciones simplemente no ayudan o no pueden ser apropiadas!
Por ejemplo, ayer descubrí que en una de mis funciones (que funciona con Excel) Excel sigue ejecutándose después de que finaliza la función. Lo intenté todo! ¡Revisé a fondo toda la función 10 veces y agregué Marshal.FinalReleaseComObject () para todo! También tenía GC.Collect () y GC.WaitForPendingFinalizers (). Revisé los cuadros de mensajes ocultos. Traté de enviar el mensaje WM_CLOSE a la ventana principal de Excel. Ejecuté mi función en un dominio de aplicación separado y descargué ese dominio. ¡Nada ayudó! La opción de cerrar todas las instancias de Excel es inapropiada, porque si el usuario inicia otra instancia de Excel manualmente, durante la ejecución de mi función, que también funciona con Excel, esa función también cerrará esa instancia. ¡Apuesto a que el usuario no será feliz! Entonces, honestamente, esta es una opción poco convincente (sin ofender, muchachos).Solución : elimine el proceso de Excel por hWnd de su ventana principal (es la primera solución).
Aquí está el código simple:
Como puede ver, proporcioné dos métodos, de acuerdo con el patrón Try-Parse (creo que es apropiado aquí): un método no arroja la excepción si el Proceso no puede ser eliminado (por ejemplo, el proceso ya no existe) , y otro método arroja la excepción si el Proceso no se eliminó. El único punto débil en este código son los permisos de seguridad. Teóricamente, el usuario puede no tener permisos para eliminar el proceso, pero en el 99.99% de todos los casos, el usuario tiene dichos permisos. También lo probé con una cuenta de invitado: funciona perfectamente.
Entonces, su código, trabajando con Excel, puede verse así:
Voila! ¡Excel ha terminado! :)
Ok, volvamos a la segunda solución, como prometí al principio de la publicación. La segunda solución es llamar a GC.Collect () y GC.WaitForPendingFinalizers (). Sí, en realidad funcionan, ¡pero debes tener cuidado aquí!
Mucha gente dice (y dije) que llamar a GC.Collect () no ayuda. ¡Pero la razón por la que no ayudaría es si todavía hay referencias a objetos COM! Una de las razones más populares para que GC.Collect () no sea útil es ejecutar el proyecto en modo de depuración. En el modo de depuración, los objetos a los que ya no se hace referencia realmente no se recogerán como basura hasta el final del método.
Por lo tanto, si probó GC.Collect () y GC.WaitForPendingFinalizers () y no funcionó, intente hacer lo siguiente:
1) Intente ejecutar su proyecto en modo Release y verifique si Excel se cerró correctamente
2) Envuelva el método de trabajar con Excel en un método separado. Entonces, en lugar de algo como esto:
usted escribe:
Ahora, Excel se cerrará =)
fuente
ACTUALIZACIÓN : código C # agregado y enlace a trabajos de Windows
Pasé algún tiempo tratando de resolver este problema, y en ese momento XtremeVBTalk era el más activo y receptivo. Aquí hay un enlace a mi publicación original, Cerrando un proceso de Interoperabilidad de Excel limpiamente, incluso si su aplicación falla . A continuación se muestra un resumen de la publicación y el código copiado en esta publicación.
Application.Quit()
yProcess.Kill()
funciona en su mayor parte, pero falla si las aplicaciones se bloquean catastróficamente. Es decir, si la aplicación falla, el proceso de Excel seguirá ejecutándose.Encontré que esta es una solución limpia porque el sistema operativo está haciendo un verdadero trabajo de limpieza. Todo lo que tienes que hacer es registrar el proceso de Excel.
Código de trabajo de Windows
Envuelve las llamadas a la API Win32 para registrar procesos de interoperabilidad.
Nota sobre el código del constructor
info.LimitFlags = 0x2000;
se llama a.0x2000
es elJOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
valor de enumeración, y MSDN define este valor como:Llamada de API Win32 adicional para obtener la ID de proceso (PID)
Usando el código
fuente
Esto funcionó para un proyecto en el que estaba trabajando:
Aprendimos que era importante establecer todas las referencias a un objeto COM de Excel en nulo cuando terminara con él. Esto incluía celdas, hojas y todo.
fuente
Cualquier cosa que esté en el espacio de nombres de Excel debe ser liberada. Período
No puedes estar haciendo:
Tienes que estar haciendo
seguido de la liberación de los objetos.
fuente
Primero, nunca tiene que llamar
Marshal.ReleaseComObject(...)
niMarshal.FinalReleaseComObject(...)
cuando interopera con Excel. Es un antipatrón confuso, pero cualquier información sobre esto, incluida la de Microsoft, que indique que debe liberar manualmente las referencias COM de .NET es incorrecta. El hecho es que el tiempo de ejecución de .NET y el recolector de basura registran y limpian correctamente las referencias COM. Para su código, esto significa que puede eliminar todo el ciclo `while (...) en la parte superior.En segundo lugar, si desea asegurarse de que las referencias COM a un objeto COM fuera de proceso se limpien cuando finalice su proceso (para que se cierre el proceso de Excel), debe asegurarse de que se ejecuta el recolector de basura. Hace esto correctamente con llamadas a
GC.Collect()
yGC.WaitForPendingFinalizers()
. Llamar a esto dos veces es seguro y garantiza que los ciclos también se limpien definitivamente (aunque no estoy seguro de que sea necesario, y agradecería un ejemplo que lo demuestre).Tercero, cuando se ejecuta bajo el depurador, las referencias locales se mantendrán artificialmente vivas hasta el final del método (para que funcione la inspección de variables locales). Por lo tanto, las
GC.Collect()
llamadas no son efectivas para limpiar objetos comorng.Cells
del mismo método. Debería dividir el código haciendo la interoperabilidad COM desde la limpieza del GC en métodos separados. (Este fue un descubrimiento clave para mí, de una parte de la respuesta publicada aquí por @nightcoder).El patrón general sería así:
Hay mucha información falsa y confusión sobre este problema, incluidas muchas publicaciones en MSDN y en Stack Overflow (¡y especialmente esta pregunta!).
Lo que finalmente me convenció de echar un vistazo más de cerca y descubrir el consejo correcto fue la publicación de blog Marshal .
fuente
Me encontré con una plantilla genérica útil que ayuda puede implementar el patrón de eliminación correcto para objetos COM, esa necesidad Marshal.ReleaseComObject llama cuando salen del ámbito de aplicación:
Uso:
Modelo:
Referencia:
http://www.deez.info/sengelha/2005/02/11/useful-idisposable-class-3-autoreleasecomobject/
fuente
No puedo creer que este problema haya atormentado al mundo durante 5 años ... Si ha creado una aplicación, primero debe cerrarla antes de eliminar el enlace.
al cerrar
Cuando nuevas aplicaciones de Excel, se abre un programa de Excel en segundo plano. Debe ordenar que se cierre ese programa de Excel antes de liberar el enlace porque ese programa de Excel no es parte de su control directo. Por lo tanto, permanecerá abierto si se libera el enlace.
Buena programación para todos ~~
fuente
Desarrolladores comunes, ninguna de sus soluciones funcionó para mí, así que decido implementar un nuevo truco .
Primero dejemos especificar "¿Cuál es nuestro objetivo?" => "No ver el objeto de Excel después de nuestro trabajo en el administrador de tareas"
Okay. Deje que no lo desafíe y comience a destruirlo, pero considere no destruir otras instancias de Excel que se ejecutan en paralelo.
Entonces, obtenga la lista de procesadores actuales y obtenga el PID de los procesos de EXCEL, luego, una vez que haya terminado su trabajo, tenemos un nuevo invitado en la lista de procesos con un PID único, encuentre y destruya solo ese.
<tenga en cuenta que cualquier nuevo proceso de Excel durante su trabajo de Excel se detectará como nuevo y destruido> <Una mejor solución es capturar el PID del nuevo objeto de Excel creado y simplemente destruirlo>
Esto resuelve mi problema, espero el tuyo también.
fuente
Esto parece seguro que ha sido demasiado complicado. Según mi experiencia, solo hay tres cosas clave para que Excel se cierre correctamente:
1: asegúrese de que no haya referencias restantes a la aplicación de Excel que creó (solo debe tener una de todas formas; configúrela en
null
)2: llamada
GC.Collect()
3: Excel tiene que cerrarse, ya sea por el usuario cerrando manualmente el programa o llamando
Quit
al objeto Excel. (Tenga en cuenta queQuit
funcionará como si el usuario intentara cerrar el programa y presentará un cuadro de diálogo de confirmación si hay cambios no guardados, incluso si Excel no está visible. El usuario podría presionar cancelar, y luego Excel no se habrá cerrado. )1 debe suceder antes que 2, pero 3 puede suceder en cualquier momento.
Una forma de implementar esto es envolver el objeto de interoperabilidad de Excel con su propia clase, crear la instancia de interoperabilidad en el constructor e implementar IDisposable con Dispose con un aspecto similar a
Eso limpiará Excel del lado de las cosas de su programa. Una vez que Excel esté cerrado (manualmente por el usuario o por usted llamando
Quit
), el proceso desaparecerá. Si el programa ya se ha cerrado, el proceso desaparecerá en laGC.Collect()
llamada.(No estoy seguro de lo importante que es, pero es posible que desee una
GC.WaitForPendingFinalizers()
llamada después de laGC.Collect()
llamada, pero no es estrictamente necesario deshacerse del proceso de Excel).Esto me ha funcionado sin problemas durante años. Sin embargo, tenga en cuenta que si bien esto funciona, en realidad tiene que cerrar con gracia para que funcione. Seguirá acumulando procesos de excel.exe si interrumpe su programa antes de que se limpie Excel (generalmente presionando "detener" mientras se está depurando su programa).
fuente
Marshal
.Para agregar a las razones por las cuales Excel no se cierra, incluso cuando crea referencias directas a cada objeto al leer, la creación, es el ciclo 'For'.
fuente
Tradicionalmente he seguido los consejos que se encuentran en la respuesta de VVS . Sin embargo, en un esfuerzo por mantener esta respuesta actualizada con las últimas opciones, creo que todos mis proyectos futuros utilizarán la biblioteca "NetOffice".
NetOffice es un reemplazo completo para los PIA de Office y es completamente independiente de la versión. Es una colección de envoltorios COM administrados que pueden manejar la limpieza que a menudo causa dolores de cabeza cuando se trabaja con Microsoft Office en .NET.
Algunas características clave son:
De ninguna manera estoy afiliado al proyecto; Realmente aprecio la reducción absoluta de los dolores de cabeza.
fuente
La respuesta aceptada aquí es correcta, pero también tenga en cuenta que no solo se deben evitar las referencias de "dos puntos", sino también los objetos que se recuperan a través del índice. Tampoco necesita esperar hasta que haya terminado con el programa para limpiar estos objetos, es mejor crear funciones que los limpien tan pronto como haya terminado con ellos, cuando sea posible. Aquí hay una función que creé que asigna algunas propiedades de un objeto Style llamado
xlStyleHeader
:Tenga en cuenta que tuve que establecer
xlBorders[Excel.XlBordersIndex.xlEdgeBottom]
una variable para limpiar eso (no debido a los dos puntos, que se refieren a una enumeración que no necesita ser liberada, sino porque el objeto al que me refiero es en realidad un objeto Border eso necesita ser lanzado).Este tipo de cosas no es realmente necesario en las aplicaciones estándar, que hacen un gran trabajo de limpieza después de sí mismas, pero en las aplicaciones ASP.NET, si omite incluso una de estas, no importa con qué frecuencia llame al recolector de basura, Excel lo hará todavía se está ejecutando en su servidor.
Requiere mucha atención al detalle y muchas ejecuciones de prueba mientras se monitorea el Administrador de tareas al escribir este código, pero hacerlo le ahorra la molestia de buscar desesperadamente a través de páginas de código para encontrar la instancia que se perdió. Esto es especialmente importante cuando se trabaja en bucles, donde necesita liberar CADA INSTANCIA de un objeto, a pesar de que usa el mismo nombre de variable cada vez que se repite.
fuente
Después de intentar
GC.Collect()
yGC.WaitForPendingFinalizers()
dos veces al finalla solución final que funciona para mí es mover un conjunto de
que agregamos al final de la función a un contenedor, de la siguiente manera:
fuente
Seguí esto exactamente ... Pero todavía me encontré con problemas 1 de cada 1000 veces. Quién sabe por qué. Es hora de sacar el martillo ...
Inmediatamente después de que se instancia la clase de Aplicación Excel, obtengo el proceso Excel que se acaba de crear.
Luego, una vez que he realizado toda la limpieza COM anterior, me aseguro de que el proceso no se esté ejecutando. Si todavía se está ejecutando, mátalo!
fuente
¨ ° º¤ø „¸ Dispara Excel proc y mastica chicle ¸„ ø¤º ° ¨
fuente
Debe tener en cuenta que Excel también es muy sensible a la cultura en la que se está ejecutando.
Es posible que necesite establecer la cultura en EN-US antes de llamar a las funciones de Excel. Esto no se aplica a todas las funciones, pero a algunas de ellas.
Esto se aplica incluso si está utilizando VSTO.
Para más detalles: http://support.microsoft.com/default.aspx?scid=kb;en-us;Q320369
fuente
"Nunca use dos puntos con objetos COM" es una gran regla general para evitar fugas de referencias COM, pero Excel PIA puede provocar fugas en más formas que aparentes a primera vista.
Una de estas formas es suscribirse a cualquier evento expuesto por cualquiera de los objetos COM del modelo de objetos de Excel.
Por ejemplo, suscribirse al evento WorkbookOpen de la clase Aplicación.
Alguna teoría sobre eventos COM
Las clases COM exponen un grupo de eventos a través de interfaces de devolución de llamada. Para suscribirse a eventos, el código del cliente puede simplemente registrar un objeto que implementa la interfaz de devolución de llamada y la clase COM invocará sus métodos en respuesta a eventos específicos. Dado que la interfaz de devolución de llamada es una interfaz COM, es deber del objeto de implementación disminuir el recuento de referencias de cualquier objeto COM que reciba (como parámetro) para cualquiera de los controladores de eventos.
Cómo Excel PIA expone los eventos COM
Excel PIA expone los eventos COM de la clase de aplicación Excel como eventos .NET convencionales. Cada vez que el código del cliente se suscribe a un evento .NET (énfasis en 'a'), PIA crea una instancia de una clase que implementa la interfaz de devolución de llamada y la registra con Excel.
Por lo tanto, varios objetos de devolución de llamada se registran con Excel en respuesta a diferentes solicitudes de suscripción del código .NET. Un objeto de devolución de llamada por suscripción de evento.
Una interfaz de devolución de llamada para el manejo de eventos significa que, PIA tiene que suscribirse a todos los eventos de interfaz para cada solicitud de suscripción a eventos .NET. No puede escoger y elegir. Al recibir una devolución de llamada de evento, el objeto de devolución de llamada verifica si el controlador de eventos .NET asociado está interesado en el evento actual o no y luego invoca el controlador o ignora silenciosamente la devolución de llamada.
Efecto en los recuentos de referencia de instancia COM
Todos estos objetos de devolución de llamada no disminuyen el recuento de referencias de ninguno de los objetos COM que reciben (como parámetros) para ninguno de los métodos de devolución de llamada (incluso para los que se ignoran en silencio). Dependen únicamente del recolector de basura CLR para liberar los objetos COM.
Dado que la ejecución de GC no es determinista, esto puede llevar a retrasar el proceso de Excel durante más tiempo del deseado y crear una impresión de una "pérdida de memoria".
Solución
La única solución a partir de ahora es evitar el proveedor de eventos de PIA para la clase COM y escribir su propio proveedor de eventos que libera de manera determinista los objetos COM.
Para la clase de aplicación, esto se puede hacer implementando la interfaz AppEvents y luego registrando la implementación con Excel mediante la interfaz IConnectionPointContainer . La clase de aplicación (y para el caso todos los objetos COM que exponen eventos usando el mecanismo de devolución de llamada) implementa la interfaz IConnectionPointContainer.
fuente
Como otros han señalado, debe crear una referencia explícita para cada objeto de Excel que use y llamar a Marshal.ReleaseComObject sobre esa referencia, como se describe en este artículo de KB . También debe usar try / finally para asegurarse de que siempre se llama a ReleaseComObject, incluso cuando se produce una excepción. Es decir, en lugar de:
necesitas hacer algo como:
También debe llamar a Application.Quit antes de liberar el objeto Aplicación si desea que Excel se cierre.
Como puede ver, esto rápidamente se vuelve extremadamente difícil de manejar tan pronto como intenta hacer algo, incluso moderadamente complejo. He desarrollado con éxito aplicaciones .NET con una clase de contenedor simple que envuelve algunas manipulaciones simples del modelo de objetos de Excel (abrir un libro de trabajo, escribir en un rango, guardar / cerrar el libro de trabajo, etc.). La clase contenedora implementa IDisposable, implementa cuidadosamente Marshal.ReleaseComObject en cada objeto que usa, y no expone públicamente ningún objeto de Excel al resto de la aplicación.
Pero este enfoque no escala bien para requisitos más complejos.
Esta es una gran deficiencia de .NET COM Interop. Para escenarios más complejos, consideraría seriamente escribir una DLL ActiveX en VB6 u otro lenguaje no administrado en el que pueda delegar toda interacción con objetos COM fuera de proceso como Office. Luego puede hacer referencia a esta DLL ActiveX desde su aplicación .NET, y las cosas serán mucho más fáciles ya que solo necesitará liberar esta referencia.
fuente
Cuando todo lo anterior no funcionó, intente darle a Excel algo de tiempo para cerrar sus hojas:
fuente
¡Asegúrese de liberar todos los objetos relacionados con Excel!
Pasé unas horas intentando de varias maneras. Todas son grandes ideas, pero finalmente encontré mi error: si no liberas todos los objetos, ninguna de las formas anteriores puede ayudarte en mi caso. ¡Asegúrate de liberar todos los objetos, incluido el rango uno!
Las opciones están juntas aquí .
fuente
Un gran artículo sobre la liberación de objetos COM es 2.5 Liberación de objetos COM (MSDN).
El método que recomendaría es anular su Excel. Interoperar referencias si son variables no locales, y luego llamar
GC.Collect()
yGC.WaitForPendingFinalizers()
dos veces. Las variables de interoperabilidad de ámbito local se ocuparán automáticamente.Esto elimina la necesidad de mantener una referencia con nombre para cada objeto COM.
Aquí hay un ejemplo tomado del artículo:
Estas palabras son directamente del artículo:
fuente
La regla de los dos puntos no funcionó para mí. En mi caso, creé un método para limpiar mis recursos de la siguiente manera:
fuente
Mi solución
fuente
Debe tener mucho cuidado al usar aplicaciones de interoperabilidad de Word / Excel. Después de probar todas las soluciones, todavía quedaba mucho proceso "WinWord" abierto en el servidor (con más de 2000 usuarios).
Después de trabajar en el problema durante horas, me di cuenta de que si abro más de un par de documentos usando
Word.ApplicationClass.Document.Open()
diferentes hilos simultáneamente, ¡el proceso de trabajo de IIS (w3wp.exe) se bloquearía dejando todos los procesos de WinWord abiertos!Así que supongo que no hay una solución absoluta para este problema, sino cambiar a otros métodos, como el desarrollo de Office Open XML .
fuente
La respuesta aceptada no funcionó para mí. El siguiente código en el destructor hizo el trabajo.
fuente
Actualmente estoy trabajando en la automatización de Office y he encontrado una solución para esto que siempre funciona para mí. Es simple y no implica matar ningún proceso.
Parece que simplemente recorriendo los procesos activos actuales y de alguna manera 'accediendo' a un proceso abierto de Excel, se eliminará cualquier instancia perdida de Excel. El siguiente código simplemente verifica los procesos donde el nombre es 'Excel', luego escribe la propiedad MainWindowTitle del proceso en una cadena. Esta 'interacción' con el proceso parece hacer que Windows se ponga al día y anule la instancia congelada de Excel.
Ejecuto el siguiente método justo antes del complemento que estoy desarrollando para salir, ya que activa el evento de descarga. Elimina todas las instancias colgantes de Excel cada vez. Honestamente, no estoy completamente seguro de por qué esto funciona, pero funciona bien para mí y podría colocarse al final de cualquier aplicación de Excel sin tener que preocuparme por los puntos dobles, Marshal.ReleaseComObject, ni los procesos de eliminación. Estaría muy interesado en cualquier sugerencia de por qué esto es efectivo.
fuente
Creo que algo de eso es solo la forma en que el marco maneja las aplicaciones de Office, pero podría estar equivocado. Algunos días, algunas aplicaciones limpian los procesos de inmediato, y otros días parece esperar hasta que se cierra la aplicación. En general, dejé de prestar atención a los detalles y me aseguré de que no haya procesos adicionales flotando al final del día.
Además, y tal vez haya terminado de simplificar las cosas, pero creo que puedes ...
Como dije antes, no tiendo a prestar atención a los detalles de cuándo aparece o desaparece el proceso de Excel, pero eso generalmente funciona para mí. Tampoco me gusta mantener los procesos de Excel por un tiempo que no sea la cantidad mínima de tiempo, pero probablemente estoy siendo paranoico al respecto.
fuente
Como algunos probablemente ya hayan escrito, no solo es importante cómo cerrar el Excel (objeto); También es importante cómo abres y también por el tipo de proyecto.
En una aplicación WPF, básicamente el mismo código funciona sin o con muy pocos problemas.
Tengo un proyecto en el que el mismo archivo de Excel se procesa varias veces para diferentes valores de parámetros, por ejemplo, analizarlo en función de los valores dentro de una lista genérica.
Puse todas las funciones relacionadas con Excel en la clase base, y analizador en una subclase (diferentes analizadores utilizan funciones comunes de Excel). No quería que Excel se abriera y cerrara nuevamente para cada elemento en una lista genérica, así que lo abrí solo una vez en la clase base y lo cerré en la subclase. Tuve problemas al mover el código a una aplicación de escritorio. He probado muchas de las soluciones mencionadas anteriormente.
GC.Collect()
ya se implementó antes, el doble de lo sugerido.Luego decidí mover el código para abrir Excel a una subclase. En lugar de abrir solo una vez, ahora creo un nuevo objeto (clase base) y abro Excel para cada elemento y lo cierro al final. Hay algunas penalizaciones de rendimiento, pero en función de varias pruebas, los procesos de Excel se están cerrando sin problemas (en modo de depuración), por lo que también se eliminan los archivos temporales. Continuaré con las pruebas y escribiré un poco más si recibo algunas actualizaciones.
La conclusión es: también debe verificar el código de inicialización, especialmente si tiene muchas clases, etc.
fuente