调用 Word.Documents.Add 后 WinWord.exe 不会退出 - Word .NET Interop

发布于 2024-08-26 14:51:44 字数 1310 浏览 5 评论 0原文

我遇到了一个经典场景,即在 .NET 中创建 Word COM 对象(通过 Microsoft.Office.Interop.Word 程序集)时,即使我 正确关闭和释放对象

我已将范围缩小到使用 Word.Documents.Add() 方法。我可以以其他方式使用 Word,不会出现任何问题(打开文档、修改内容等),并且当我告诉 WinWord.exe 退出时,它就会退出。一旦我使用 Add() 方法(并且仅在添加模板时),该进程就会继续运行。

下面是一个重现该问题的简单示例:

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

如您所见,我正在正确创建和处置对象,甚至采取额外的步骤来循环 Marsha.ReleaseComObject 直到它返回正确的代码。使用 Word 对象在其他方面都很好,只是令人讨厌的 Documents.Add 让我感到悲伤。在此过程中是否创建了另一个我需要引用和处置的对象?我还需要遵循其他处置步骤吗?还有别的事吗?非常感谢您的帮助:)

更新: 我在处置步骤结束时尝试了 GC.Collect 但仍然没有运气。

更新 2: 我已将问题范围缩小到自定义模板的使用。当我调用 Documents.Add(...) 时,我为新文档指定一个自定义模板。如果我不这样做,而是调用不带参数的 Add(),那么问题就不会发生。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

め可乐爱微笑 2024-09-02 14:51:44

(我的所有建议均改编自这个关于 Excel 互操作的答案。)

这里有一些重要的事情:

1)切勿在同一直线上使用 2 个点。还可以将索引器视为一个点

Good

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

BAD

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

2) 释放所有指针。

3)不,真的,返回并释放所有的指针,你在某个地方错过了一个(或者至少我总是这样做)。

这是一个完整的例子,展示了在我痛苦地哀嚎和咬牙切齿之后,最终在一个项目上为我工作的内容:

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

(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);
滥情空心 2024-09-02 14:51:44

您尝试过更改

oWord.Visible = False

为吗

oWord.Visible = True

我之所以这么问,是因为 Word 可能会要求您执行与您尝试使用的此模板相关的操作。如果它认为有一个对话框显示,它通常不会关闭。 IIRC,有一种方法可以执行退出,以便它强制退出并且不会等待任何对话框。但是,已经有一段时间了。

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.

幸福丶如此 2024-09-02 14:51:44

我在做的时候遇到了同样的问题:

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

我是这样解决的:

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

不要问我为什么,自动化办公是一场冒险:)

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

翻了热茶 2024-09-02 14:51:44

我只做过Excel自动化,但也遇到过类似的问题。参考一些旧代码,关闭的最后一步有 GC.Collect() 行,

这篇文章也提到了它:
http://support.microsoft.com/kb/317109

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

恏ㄋ傷疤忘ㄋ疼 2024-09-02 14:51:44

尝试调用 GC.WaitForPendingFinalizers() 并使用 Marshal.FinalReleaseComObject 而不是 Marshal.ReleaseComObject。这消除了循环它的需要。

将您的代码更新为此并尝试一下(GC 调用是故意在开头的):

GC.Collect()
GC.WaitForPendingFinalizers()

oDoc.Close()
Marshal.FinalReleaseComObject(oDoc)

Marshal.FinalReleaseComObject(oDocuments)

oWord.Quit()
Marshal.FinalReleaseComObject(oWord)

您可能还想查看 此相关问题讨论 Excel 的问题。

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.

千纸鹤带着心事 2024-09-02 14:51:44

我发现使用自定义模板时使用 Documents.Add() 是罪魁祸首。我无法解释为什么这会让 WinWord.exe 挂起。但是,还有其他方法可以从模板创建文档,但不会导致相同的问题。

因此,我将: 替换

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

为:

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

使用 AttachedTemplate 来指定适合我的模板,并且不会让 WinWord.exe 挂起。

(然而,出现了一个新问题......使用 AttachedTemplate/UpdateStyles 时,模板页脚中的图像不会复制到文档中。我将其作为一个单独的问题来解决。但由于此方法解决了我原来的问题,我感谢所有提供答案的人!)

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

世态炎凉 2024-09-02 14:51:44

由于模板存在类似问题,我看到了您的帖子。每当我尝试关闭程序中的 Word 时,都会收到一条消息,提示我保存 .dotm 文件。我无法使用您接受的答案,因为我没有确切的模板路径,我只是打开程序收到的任何文档。

我使用的是,

Word.NormalTemplate.Saved = true;

当我在处理应用程序之前使用该代码时,它不会再弹出对话框说我没有保存模板,并且它将运行处理而不会留下不需要的“winWord.exe” ”进程正在运行。

我从 Visual Basic 论坛上的用户“NeedSomeAnswers”那里获得了“NormalTemplate.Saved”提示 此处。用他的话来说,“[它]实际上并不保存到法线,它只是告诉 Word 法线已经保存,因此不需要保存它”。

我认为这是同一问题的第二个答案。我希望它有帮助。

祝你有美好的一天,身体健康。

-你的代码工作的任何一天都是值得庆祝的好日子-

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-

旧时浪漫 2024-09-02 14:51:44

“oDocuments”有 .Dispose() 或 .Close() 方法吗?你要处理另外两个,但不能处理这个。

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

心欲静而疯不止 2024-09-02 14:51:44

虽然这是 C#,但也许它会对你有所帮助。我正在使用这种方法将多个文档合并为一个。我传递了 Arraylist 中的所有文档,完成后 Word 似乎正确关闭。

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

finally 块对于清理在 try 块中分配的所有资源以及运行即使出现异常也必须执行的任何代码非常有用。无论 try 块如何退出,控制权总是传递给 finally 块。

那么尝试将你的代码放入 try/finally 块中,看看它的行为如何?

对于VB.NET

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

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
悸初 2024-09-02 14:51:44

您不应丢弃在 Word.Documents.Add 中创建的文档对象。完成后,在从自动化获得的每个 COM 对象上保存并调用 Marshal.ReleaseComObject,即 如果您不在任何地方缓存对象

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.

差↓一点笑了 2024-09-02 14:51:44
oWord.Visible = True

为我解决了问题。根本问题是文档恢复。尽管有一行,但还是出现了一个对话框:

_wordApp.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;

我使用了此处显示的所有技巧,但在文档恢复列表被清除之前,每次我的应用程序运行时都会留下一个“僵尸”字进程。

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.

九公里浅绿 2024-09-02 14:51:44

这是一个完美的解决方案,我遇到了同样的问题,我只是按照这个解决方案,它工作得很好。

对象 objFalse = false;

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

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

伴我心暖 2024-09-02 14:51:44

我曾尝试在 vb.net 中自动创建 Word 文档,但即使在我关闭文档后,winword.exe 仍在运行。我偶然发现了这个问题的解决方案;我将单词对象的暗淡部分移到了用于编辑文档的子例程内部,而不是独立于子例程(我的初始方法)对其进行尺寸标注。

希望这有帮助。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文