2011-04-09 7 views
6

Al usar COM Interop con Office (generalmente Excel), siempre me aseguro cuidadosamente de llamar al Marshal.ReleaseComObject en cada referencia, para evitar el problema donde Excel no abandona as described in this KB article.Cómo liberar objetos COM en Silverlight 4

¿Cómo puedo garantizar que Excel se cierra cuando uso Interop desde una aplicación OOB Silverlight (con AutomationFactory.CreateObject)?

Silverlight no tiene un método Marshal.ReleaseComObject e incluso llamar a GC.Collect y GC.WaitForPendingFinalizers no ayuda.

¿Seguro que Microsoft no ha agregado esta característica a Silverlight sin un mecanismo para liberar las referencias COM? Esto me parece ser una maravilla para la automatización de servidores COM fuera de proceso como Excel.

Un sorprendente omisión, tanto más cuanto que Pete Brown en la sección 5.5 de su libro "Silverlight 4 en Acción" va tan lejos como para decir sobre AutomationFactory.CreateObject, que:

La intención principal de esta característica es permitir la automatización de otras aplicaciones, incluida Microsoft Office.

ACTUALIZACIÓN en respuesta a los comentarios de Hans.

No estoy seguro de que exista un problema de "asesino silencioso" en la automatización típica de las aplicaciones de Office. Un uso común podría ser algo como lo siguiente, que he visto utilizar en varias ocasiones en las aplicaciones de Windows Forms, sin llegar nunca a través de la "envenenado RCW" descrito en el artículo enlazado por Hans:

  • Crear una instancia Excel.Application
  • Abrir o crear un libro
  • datos de escribir en el libro de
  • Mostrar Excel si todo iba bien, cierre el libro y llame Application.Quit si no.
  • Llame a Marshal.ReleaseComObject para liberar todas las referencias de objetos de Excel.

Si no se puede llamar a Marshal.ReleaseComObject según lo recomendado por Hans, se dejarán ejecutadas varias copias de Excel.exe, como se describe en el artículo de KB mencionado anteriormente: altamente indeseable.

ACTUALIZACIÓN 2

La muestra que estoy usando para repro esto es una muestra del código fuente para el libro de Pete Brown Silverlight 4 en la acción, hay un enlace de descarga en esta página. La solución de muestra AutomatingExcel está en Ch05.zip/5.03. Para repro:

  • Asegúrese de que no hay instancias de Excel están ejecutando
  • muestra Run AutomatingExcel
  • libro de Excel se abre
  • Cierre Excel
  • Observar con el Administrador de tareas de Excel que todavía se está ejecutando.

Configurando todas las variables dinámicas a nulo y llamando a GC.Collect() parece funcionar como se indica en la respuesta de AnthonyWJones.

ACTUALIZACIÓN 2

respuesta de Otaku es lo que estaba buscando - envolviendo referencias en una instrucción using las referencias COM son puestos en libertad sin la necesidad de llamar a GC.Collect. Un poco de experimentación muestra que es más tolerante a no disponer de cada referencia individual, a diferencia de la solución estándar Marshal.ReleaseComObject descrita en el artículo de KB mencionado anteriormente.

Sería interesante tener una opinión autorizada sobre exactamente lo que debe eliminarse para garantizar que se publiquen todas las referencias de Excel.

+0

posible duplicado de [? Cuándo utilizar RelaseComObject vs FinalReleaseComObject] (http://stackoverflow.com/ preguntas/3937181/when-to-use-relasecomobject-vs-finalreleasecomobject) –

+0

@Hans: Silverlight no tiene ninguno de estos métodos en la clase 'Marshal'. – AnthonyWJones

+0

@Anthony: explica por qué ninguno de estos métodos es una buena idea. El enlace "asesino silencioso" es muy relevante. –

Respuesta

2

Puede implementar la interfaz IDisposable. El mejor ejemplo de esto que he visto está en http://csfun.blog49.fc2.com/blog-entry-79.html. La entrada del blog está en japonés, pero se abre en Chrome y permite que Google haga la traducción de la página por usted si no lee japonés.

Además, si solo desea el ejemplo del código fuente del contenedor COM directamente, puede descargar la aplicación de ejemplo que viene en: SilverOffice.

+0

+1 gracias - esta es la respuesta que estaba buscando - cómo liberar los objetos COM sin llamar a GC.Collect. Parece que las referencias creadas con la palabra clave dinámica implementan IDisposable, y que esto libera las referencias COM. Una pena MSDN no proporciona más información explícita sobre este tema: http://msdn.microsoft.com/en-us/library/dd264733.aspx – Joe

+0

Eso es muy interesante, no había pensado en probar y usar 'IDisposable' desafortunadamente disponer no libera el objeto COM. – AnthonyWJones

+0

@Joe: Acabo de probar eso agregando Dispose a mi código. Sin GC.Collect todavía termino con múltiples instancias de Excel. – AnthonyWJones

0

Consideraría construir el archivo Excel en un servicio WCF. Puedes hacer toda la limpieza allí. Envíe a bytes a su aplicación Silverlight y use un SaveFileDialog para enviarlos al usuario.

Silverlight tiene una restricción de acceso al sistema de archivos del cliente. Manipular un archivo de Excel en el sistema del cliente, o incluso asumir que el cliente tiene Excel instalado parece una violación de esto.

+0

mientras su enfoque es válido, no responde la pregunta. Y puede haber razones legítimas para querer automatizar Excel en el lado del cliente en aplicaciones OOB de confianza elevada. – Joe

1

mirada Tomar en este código: - aparece

private void Button_Click(object sender, RoutedEventArgs e) 
    { 
     dynamic app = AutomationFactory.CreateObject("Excel.Application"); 
     dynamic book = app.Workbooks.Add(); 
     dynamic sheet = app.ActiveSheet(); 

     sheet = null; 
     book.Close(false); 
     book = null; 
     app.Quit(); 
     app = null; 

     GC.Collect(); 
    } 

El proceso de Excel y luego desaparece. Quite el GC y el proceso de Excel continuará. ¿Obtienes lo mismo si copias este código palabra por palabra? Si es así, sugeriría que en alguna parte de su código, una referencia a un objeto de Excel sigue siendo accesible desde una de las pilas de hilos o campos estáticos.

¿Alguna vez tiene un objeto Excel en un campo (en lugar de una variable local)?

¿Tiene un objeto excel en lo que parece ser una variable pero se hace referencia desde un delegado dinámico o lambda que se utiliza como controlador de eventos?

¿Está conectando controladores de eventos a objetos de larga vida útil de un objeto que tiene una corta vida útil? Si es así, ¿está asegurando que se separe de esos manejadores correctamente?

Muchas de estas cosas pueden atrapar a los desarrolladores para que dejen lo que creen que son objetos listos para GC, pero el GC los encuentra accesibles y, por lo tanto, no son candidatos para la recolección.

Si el código anterior no se comporta de la misma manera, buscamos otro problema por completo. Estoy usando Office 2007 en Server 2008 R2 desde el último tiempo de ejecución SL 4. Sin embargo, si tenemos una variante debido a la configuración, entonces estamos en un terreno muy inestable.

Editar

Con algunas pruebas de esto parece ser efectivo: -

private void Button_Click(object sender, RoutedEventArgs e) 
    { 
     using (dynamic app = AutomationFactory.CreateObject("Excel.Application")) 
     { 
      using (dynamic book = app.Workbooks.Add()) 
      { 
       using (dynamic sheet = app.ActiveSheet()) 
       { 

       } 
       book.Close(); 
      } 
      app.Quit(); 
     }; 

     GC.Collect(); 
    } 

Sin embargo dejar fuera de la GC y el resultado final será con los procesos no deseados Excel deja en marcha con el tiempo.

+0

Parece prometedor. Mi entorno objetivo es Excel 2003, pero acabo de probar tu código en Excel 2007 en casa y funciona. Establecer las variables a nulo parece ser necesario, no estoy seguro de por qué, ya que no deberían poder comunicarse de todos modos. Sin embargo, GC.Collect() parece una artillería pesada solo para liberar las referencias COM. – Joe

+0

@Joe: No necesariamente, si elimina '' x = null' de mi código, entonces, en el momento en que se llama a GC.Collect, podría ver que el proceso de Excel continúa a pesar de la llamada a 'GC.Collect'. Esto se debe a que cuando se llama a Collect, todos los hilos se suspenden y el GC inspecciona sus pilas donde puede encontrar las referencias mantenidas por las variables locales. Uso la palabra "puede" porque las optimizaciones pueden hacerlos inalcanzables. Si mueve más al código en otra función y lo llama desde el evento Click, llame a GC.Collect después de que la asignación a null no sea necesaria. – AnthonyWJones

+0

+1 y gracias por la investigación continua. Su código potencialmente tiene una instanciación implícita (el problema de "doble punto"). ¿Ha intentado "usar (libros de trabajo dinámicos = app.Workbooks)" seguido de "libros de trabajo.Add()" para asegurarse de que todas las referencias se crean y eliminan explícitamente. Hacer esto con cuidado es necesario con la interoperabilidad estándar de COM, y si no lo hace, puede explicar por qué los procesos se ejecutan eventualmente. – Joe

0

envolviendo referencias en una instrucción using las referencias COM se liberan

Tenga en cuenta: utilizando declaraciones son simplemente azúcar sintáctica para try/catch/finally con Dispose() en la cláusula finally.

Además, la mayoría de las aplicaciones no permiten el uso de declaraciones en este caso, porque la creación y la limpieza de los objetos COM se distribuyen entre varios lugares/métodos.

la línea crítica que se necesita aquí lee:

((IDisposable)_excel).Dispose(); // Release COM Interface 

Esto supone:

dynamic _excel = AutomationFactory.CreateObject("Excel.Application"); 
Cuestiones relacionadas