开发者

WinWord.exe won't quit after calling Word.Documents.Add - Word .NET Interop

I'm running into the classic scenario where, when creating Word COM objects in .NET (via the Microsoft.Office.Interop.Word assembly), the WinWord process won't exit even though I'm properly closing and releasing the objects.

I've narrowed it down to the use of the Word.Documents.Add() method. I can work with Word in other ways without a problem (opening documents, modifying contents, etc) and WinWord.exe quits when I tell it to. It's once I use the Add() method (and only when adding a template) that the process is left running.

Here is a simple example which reproduces the problem:

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()

As you can see I'm creating and disposing the objects properly, even taking the extra step to loop Marsha.ReleaseComObject until it returns the proper code. Working with the Word objects is fine in other regards, it's just that pesky Documents.Add that is causing me grief. Is there another object that gets created in this process that I need to reference and dispose of? Is there another disposal step I need to follow? Something else? Your help is much appreciated :)

Update: I tried GC.Collect at the end of the disposal step but still no luck.

Update 2: I've narrowed the problem down to the use of custom templates. When I invoke Documents.Add(...) I specify a custom template for the new document. If I don't do this and instead invoke Add() with no parameters, then the problem does not happen.


(All of my advice is adapted from this answer about Excel interop.)

There are a few important things here:

1) Never use 2 dots on the same line. Also consider an indexer as a dot

Good

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

BAD

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

2) Release all of your pointers.

3) No really, go back and release all of your pointers, you missed one somewhere (or at least I always do).

Here's a full example of what FINALLY worked for me on one project after much wailing and gnashing of teeth:

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);


Have you tried changing

oWord.Visible = False

to

oWord.Visible = True

?

I ask because Word may be asking you to do something that's related to this template you are trying to use. If it thinks there's a dialog showing, it will normally not shut down. IIRC, there's a way to do Quit so that it forces Quit and won't wait on any dialogs. But, it's been a while.


I got the same problema when i was doing it:

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

I solved this way:

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

Don't ask me why, automating office is an adventure :)


I've only done Excel automation, but have run into similar problems. Referencing some old code, the final step in closing has the line GC.Collect()

This article mentions it too: http://support.microsoft.com/kb/317109


Try calling GC.WaitForPendingFinalizers() and using Marshal.FinalReleaseComObject instead of Marshal.ReleaseComObject. This gets rid of the need to loop it.

Update your code to this and try it (the GC calls are in the beginning on purpose):

GC.Collect()
GC.WaitForPendingFinalizers()

oDoc.Close()
Marshal.FinalReleaseComObject(oDoc)

Marshal.FinalReleaseComObject(oDocuments)

oWord.Quit()
Marshal.FinalReleaseComObject(oWord)

You might also want to check out this related question discussing the issue for Excel.


I figured out that the use of Documents.Add() when using a custom template is to blame. I can't explain why this would leave WinWord.exe hanging. However there are other ways to create documents from templates that don't result in the same problem.

So I replaced:

Dim doc As Word.Document = documents.Add(Template:=CObj(templatePath))

with:

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

Using AttachedTemplate to specify the template works for me and doesn't leave WinWord.exe hanging.

(One new issue has arisen however... An image in the template's footer does not get copied to the document when using AttachedTemplate/UpdateStyles. I'm taking that up as a separate issue. But since this method solves my original problem, I'm satisfied. Thanks to everyone who offered answers!)


I came across your post because of a similar issue with the template. I would get a message prompting me to save the .dotm file whenever I would try to close word in my program. I couldn't use your accepted answer because I don't have an exact template path, I just open whatever document the program receives.

what I used is

Word.NormalTemplate.Saved = true;

when I used that code before I disposed of the application, it would no longer bring up the dialog saying I hadn't saved the template, and it would run the disposal without leaving the unwanted "winWord.exe" process running.

I got the "NormalTemplate.Saved" tip from the user "NeedSomeAnswers" on the visual basic forums here. In his words "[it] doesn't actually save to the Normal, it just tells Word that the Normal has already been saved so it doesn't need to save it".

I think this is a second answer to the same problem. I hope it helps.

Have an awesome day, and be well.

-any day your code works is a good day to celebrate-


does "oDocuments" have a .Dispose() or .Close() method? you're disposing of the other 2, but not this one.


Although this is C# but maybe it will help you out. I'm using this method to merge multiple documents into one. I pass all documents in Arraylist, and Word seems to close properly when done.

 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);
        }
    }

The finally block is useful for cleaning up any resources allocated in the try block as well as running any code that must execute even if there is an exception. Control is always passed to the finally block regardless of how the try block exits.

So try to put your code into try / finally block and see how it behaves then?

For VB.NET

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


You should not discard the document object created in Word.Documents.Add. Save and call Marshal.ReleaseComObject on every COM object you get from automation when you are done, that is, if you do not cache the objects anywhere.


oWord.Visible = True

Solved the problem for me. The underlying issue was document recovery. A dialog was appearing despite having a line:

_wordApp.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;

I used every trick that has been shown here but until the document recovery list was cleared a "zombie" word process was left behind each time my application ran.


this is a perfect solution, i had same problem, i just followed this one and it is working perfect.

object objFalse = false;

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


I had tried to automate a document's creation in word from vb.net, but winword.exe was still running, even after I closed the document. I stumbled upon a solution to this problem; I moved the dim of the word object to inside the subroutine I was using to edit the document, as opposed to dimensioning it independent of a subroutine (my initial method).

Hope this helps.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜