2010-03-24 14 views
18

Me estoy encontrando con el escenario clásico donde, al crear objetos Word COM en .NET (a través de Microsoft.Office.Interop.Word ensamblado), el proceso de WinWord no se cerrará a pesar de que estoy properly closing and releasing the objects.WinWord.exe no se cerrará después de llamar Word.Documents.Add - Word .NET Interop

Lo he reducido al uso del método Word.Documents.Add(). Puedo trabajar con Word de otras formas sin problemas (abrir documentos, modificar contenidos, etc.) y WinWord.exe se cierra cuando se lo digo. Una vez que uso el método Add() (y solo cuando agrego una plantilla ), el proceso se deja en ejecución.

Aquí está un ejemplo simple que reproduce el problema:

Dim word As New Word.Application() 
word.Visible = False 

Dim documents As Word.Documents = word.Documents 
Dim doc As Word.Document = documents.Add(Template:=CObj(templatePath), NewTemplate:=False, DocumentType:=Word.WdNewDocumentType.wdNewBlankDocument, Visible:=False) 

'' dispose objects 
doc.Close() 
While (Marshal.ReleaseComObject(doc) <> 0) 
End While 
doc = Nothing 

While (Marshal.ReleaseComObject(documents) <> 0) 
End While 
documents = Nothing 

word.Quit() 
While (Marshal.ReleaseComObject(word) <> 0) 
End While 
word = Nothing 

GC.Collect() 

Como se puede ver que estoy creando y desechar los objetos correctamente, incluso teniendo un paso más para Marsha.ReleaseComObject bucle hasta que vuelve el buen código. Trabajar con los objetos de Word está bien en otros aspectos, es solo ese molesto Documents.Add que me está causando dolor. ¿Hay algún otro objeto que se cree en este proceso que necesite referenciar y eliminar? ¿Hay algún otro paso de eliminación que deba seguir? ¿Algo más? Su ayuda es muy apreciada :)

Update: Intenté GC.Colgar al final del paso de eliminación, pero aún no tuve suerte.

Update 2: Reduje el problema al uso de plantillas personalizadas. Cuando invoco Documents.Add (...) especifico una plantilla personalizada para el nuevo documento. Si no hago esto y en su lugar invoco Agregar() sin parámetros, entonces el problema no ocurre.

+0

Esto podría ser un problema con una plantilla personalizada. ¿Funciona si simplemente llama a 'oWord.Documents.Add()'? –

+0

¡Gracias, gran sugerencia! SÍ FUNCIONA si solo llamo Agregar, por lo que es la plantilla personalizada la que de alguna manera está causando el problema. Profundizaré más. – Keith

+0

Si es así, quizás la solución de Jim Leonardo ayude. Haga que muestre Word lo que hace, tal vez algún cuadro de diálogo o algo esté esperando. – MadBoy

Respuesta

2

Me di cuenta de que el uso de Documents.Add() cuando se utiliza una plantilla personalizada tiene la culpa. No puedo explicar por qué esto dejaría colgado WinWord.exe. Sin embargo, hay otras formas de crear documentos a partir de plantillas que no generan el mismo problema.

Así que sustituyen:

Dim doc Como Word.Document = Documents.Add (Plantilla: = CObj (templatePath))

con:

Dim doc Como Word.Document = documents.Add()
doc.AttachedTemplate = templatePath
doc.UpdateStyles()

Usando AttachedTemplate para especificar que la plantilla funciona para mí y no deja colgado WinWord.exe.

(Sin embargo, ha surgido un nuevo problema ... Una imagen en el pie de página de la plantilla no se copia en el documento cuando se utiliza AttachedTemplate/UpdateStyles. Lo tomo como un problema aparte. Pero dado que este método resuelve problema original, estoy satisfecho Gracias a todos los que se ofreció respuestas)

+0

Tenga en cuenta que este * no * es un problema general con las plantillas, pero es específico de la plantilla que está utilizando. En general, su código original es la forma de crear documentos a partir de una plantilla. –

+0

Como seguimiento: definitivamente hay un problema de pérdida de memoria cuando se utiliza la interoperabilidad de Word para crear documentos basados ​​en plantillas, e incluso cuando se trabaja con documentos existentes creados a partir de plantillas. Creo que esto sucede porque tales documentos hacen que un objeto Template com se cree automáticamente y se agregue a la colección Templates del objeto Application, sin darle al desarrollador la oportunidad de referenciarlo y deshacerse de él. Otra solución alternativa que encontré es usar OpenXml para abrir un documento existente (no una plantilla) e insertarlo en controles de contenido. – Keith

0

¿"oDocuments" tiene un método .Dispose() o .Close()? estás desechando los otros 2, pero no este.

+0

Gracias por señalar eso. Estoy desechando el objeto oDocuments, me olvidé de ponerlo en mi ejemplo. El ejemplo está solucionado ahora, pero el problema persiste ... – Keith

+0

¿Ha intentado usar Try/Finally? Keith? – MadBoy

2

Solo he realizado la automatización de Excel, pero he tenido problemas similares. Hacer referencia a un código de edad, el paso final en el cierre tiene la línea GC.Collect()

Este artículo menciona también: http://support.microsoft.com/kb/317109

+0

Si no usa try/finally, definitivamente es imprescindible usar GC.Collect ya que tenía muchos sobres se quedan abiertos después de que las tareas hayan terminado. Sin embargo, cuando estaba usando try/finally Excel siempre se cierra correctamente. Por supuesto, finalmente, siempre tuve 3x Marshal.ReleaseComObject (...); – MadBoy

+0

Gracias. Agregué GC.Collect() al final de mi paso de eliminación, pero todavía no tuve suerte. – Keith

0

Aunque este es C#, pero tal vez le ayudará a cabo. Estoy usando este método para combinar varios documentos en uno solo. Paso todos los documentos en el Arraylist, y Word parece cerrarse correctamente cuando termina.

public static void documentsMerge(object fileName, ArrayList arrayList) { 
     // object fileName = Path.Combine(Environment.CurrentDirectory, @"NewDocument.doc"); 
     File.Delete(fileName.ToString()); 
     try { 
      wordApplication = new ApplicationClass(); 
      var doc = wordApplication.Documents.Add(ref missing, ref missing, ref missing, ref missing); 
      try { 
       doc.Activate(); 
       int count = 0; 
       foreach (var alItem in arrayList) { 
        addDocument(alItem, doc, count == 0); 
        count++; 
       } 
       // addDocument(@"D:\Projects\WordTests\ConsoleApplication1\Documents\Doc1.doc", doc) ; //, false); 
       // addDocument(@"D:\Projects\WordTests\ConsoleApplication1\Documents\Doc2.doc", doc) ; //, true); 
       doc.SaveAs(ref fileName, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing); 
      } finally { 
       doc.Close(ref missing, ref missing, ref missing); 
      } 
     } finally { 
      wordApplication.Quit(ref missing, ref missing, ref missing); 
     } 
    } 

El bloque finalmente es útil para cleaning up los recursos asignados en el bloque try, así como ejecutar cualquier código que se debe ejecutar, incluso si no es una excepción. El control siempre se pasa al bloque finally independientemente de cómo salga el bloque try.

Así que intente poner su código en el bloque try/finally y vea cómo se comporta en ese momento?

para VB.NET

Try 
' Statement which can cause an exception. 
Catch x As Type 
' Statements for handling the exception 
Finally 
End Try 'Any cleanup code 
+0

Gracias pero agregando el try..catch..finally block no ayuda. Lo he reducido aún más al hecho de que estoy usando una plantilla personalizada ... vea las actualizaciones en mi publicación original. – Keith

2

Intente llamar GC.WaitForPendingFinalizers()Marshal.FinalReleaseComObject y usando en lugar de Marshal.ReleaseComObject. Esto elimina la necesidad de repetirlo.

actualización de su código para esto y probarlo (las llamadas GC se encuentran en el inicio a propósito):

GC.Collect() 
GC.WaitForPendingFinalizers() 

oDoc.Close() 
Marshal.FinalReleaseComObject(oDoc) 

Marshal.FinalReleaseComObject(oDocuments) 

oWord.Quit() 
Marshal.FinalReleaseComObject(oWord) 

También puede ser que desee comprobar hacia fuera this related question discutir el tema para Excel.

+0

Gracias por el consejo. No resolvió mi problema, pero parece una forma más limpia y más estable de limpiar objetos COM. – Keith

4

Ha intentado cambiar

oWord.Visible = False 

a

oWord.Visible = True 

?

Lo pido porque Word puede pedirte que hagas algo relacionado con esta plantilla que estás tratando de usar. Si cree que hay un cuadro de diálogo que muestra, normalmente no se apagará. IIRC, hay una manera de hacerlo, de modo que forme la opción Salir y no espere en ningún cuadro de diálogo. Pero, ha pasado un tiempo.

+2

Por lo general, es una buena idea establecer 'Application.DisplayAlerts = wdAlertsNone'. Esto evita que la mayoría de los cuadros de diálogo de Word aparezcan (desafortunadamente, algunos cuadros de diálogo raros aún podrían estar bloqueando, por ejemplo, un mensaje sobre herramientas de revisión desinstaladas). –

0

You should not discard the document object created in Word.Documents.Add. Guarde y llame a Marshal.ReleaseComObject en cada objeto COM que obtenga de la automatización cuando haya terminado, es decir, if you do not cache the objects anywhere.

+0

Microsoft recomienda descartar todos los objetos a los que se hace referencia (en este caso, que incluye el objeto Document) en el artículo publicado por OG: http://support.microsoft.com/kb/317109 – Keith

+0

Parece que tenemos diferentes conocimientos sobre el descarte. ¿Descartas por limpieza? –

1

que tiene el mismo problema cuando lo estaba haciendo:.!

object missing = System.Reflection.Missing.Value; 
wordApplication.Quit(ref missing, ref missing, ref missing); 

he resuelto de esta manera:

object objFalse = false; 
wordApplication.Quit(ref objFalse, ref objFalse, ref objFalse); 

No me preguntes por qué, la automatización de la oficina es una aventura :)

9

(Todos mis consejos se han adaptado de this answer sobre la interoperabilidad de Excel.)

Hay algunas cosas importantes aquí:

1) Nunca use 2 puntos en la misma línea. Ten en cuenta también un indexador como un punto

Buena

Word.Documents d = wordApp.Documents; 
Word.Document aDoc = d.Open(/*...*/); 

BAD

Word.Document aDoc = wordApp.Documents.Open(/*...*/); 

2) Soltar todos sus punteros.

3) No, realmente, retrocede y suelta todos tus punteros, te perdiste uno en alguna parte (o al menos siempre lo hago).

Aquí hay un ejemplo completo de lo que finalmente funcionó para mí en un proyecto después de mucho llanto y crujir de dientes:

object m = Missing.Value; 
// this must be an object, not a string. if you forget though, 
// intellisense will remind you 
object oFilename = @"C:\my sheet.doc"; 

object readOnly = false; 
object isVisible = false; 

Word.Application wordApp = new Word.ApplicationClass(); 
wordApp.Visible = false; 
// remember: don't use 2 dots on 1 line 
Word.Documents d = wordApp.Documents; 
Word.Document aDoc = d.Open(ref oFilename, ref m, ref readOnly, ref m, 
    ref m, ref m, ref m, ref m, ref m, ref m, ref m, ref isVisible, 
    ref m, ref m, ref m, ref m); 
aDoc.Activate(); 

object findText = "my old value"; 
object replaceText = "new and improved value"; 

object oTrue = true; 
object oFalse = false; 
object replace = 2; 
object wrap = 1; 

Word.Selection s = wordApp.Selection; 
Word.Find f = s.Find; 
f.Execute(ref findText, ref oTrue, 
    ref oTrue, ref oFalse, ref oFalse, 
    ref oFalse, ref oTrue, ref wrap, ref oFalse, 
    ref replaceText, ref replace, ref oFalse, ref oFalse, 
    ref oFalse, ref oFalse); 

aDoc.SaveAs(ref oFilename, ref m, ref m, ref m, ref m, ref m, ref m, 
    ref m, ref m, ref m, ref m, ref m, ref m, ref m, ref m, ref m); 

object doNotSaveChanges = Word.WdSaveOptions.wdDoNotSaveChanges; 
// casting here because intellisense complained of ambiguity 
(aDoc as Word._Document).Close(ref doNotSaveChanges, ref m, ref m); 

// release each in the reverse of the order in which it was first used 
// ReleaseComObject might also work as well. I haven't tested yet 
Marshal.FinalReleaseComObject(f); 
Marshal.FinalReleaseComObject(s); 
Marshal.FinalReleaseComObject(aDoc); 
Marshal.FinalReleaseComObject(d); 

// must quit app before releasing 
// again: casting because intellisense complained of ambiguity 
(wordApp as Word._Application).Quit(ref m, ref m, ref m); 
Marshal.FinalReleaseComObject(wordApp); 
+2

La respuesta de Dinah aquí es una gran guía para todo el desarrollo de interoperabilidad de Office y probablemente resuelva los problemas de la mayoría de las personas. Sin embargo, mi problema resultó ser el uso de plantillas. Me dirijo a esto en la respuesta que publiqué. – Keith

0
oWord.Visible = True 

resuelto el problema para mí. El problema subyacente fue la recuperación de documentos. Un diálogo fue apareciendo a pesar de tener una línea:

_wordApp.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone; 

que utiliza todos los trucos que se ha demostrado aquí, pero hasta que la lista de recuperación de documentos se aclaró un proceso "zombie" palabra quedó atrás cada vez que corrió mi solicitud.

+0

¿Hay algún inconveniente en establecer 'oWord.Visible = True'? – Keith

0

esta es una solución perfecta, tuve el mismo problema, acabo de seguir este y está funcionando perfecto.

objeto objFalse = false;

wordApplication.Quit (ref objFalse, ref objFalse, ref objFalse);

1

Me encontré con su publicación debido a un problema similar con la plantilla. Me llegaba un mensaje que me pedía que guardara el archivo .dotm cada vez que intentaba cerrar la palabra en mi programa. No pude usar su respuesta aceptada porque no tengo una ruta de acceso de plantilla exacta, simplemente abro el documento que recibe el programa.

lo que he utilizado es

Word.NormalTemplate.Saved = true; 

cuando utilicé ese código antes de desecharse de la aplicación, que ya no aparezca el cuadro de diálogo diciendo que no había guardado la plantilla, y se correría el desecho sin dejar el proceso no deseado "winWord.exe" ejecutándose.

que tiene la punta "NormalTemplate.Saved" de usuario "NeedSomeAnswers" en los foros de Visual Basic here. En sus palabras "[it] no se guarda en la Normal, simplemente le dice a Word que la Normal ya se ha guardado para que no tenga que guardarla".

Creo que esta es una segunda respuesta al mismo problema. Espero que ayude.

Que tengas un día increíble y que estés bien.

día -cualquier funciona su código es un buen día para celebrate-

0

había tratado de automatizar la creación de un documento de palabra de vb.net, pero winword.exe seguía en marcha, incluso después de que cerraba el documento . Encontré una solución a este problema; Moví el tenue de la palabra objeto al interior de la subrutina que estaba usando para editar el documento, en lugar de acotarlo independientemente de una subrutina (mi método inicial).

Espero que esto ayude.

Cuestiones relacionadas