20

我遇到了经典场景,在 .NET 中(通过 Microsoft.Office.Interop.Word 程序集)创建 Word COM 对象时,即使我正确关闭和释放对象,WinWord 进程也不会退出。

我已将其缩小到使用 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 让我很伤心。在这个过程中是否有另一个我需要引用和处理的对象?我还需要遵循其他处置步骤吗?还有什么?非常感谢您的帮助 :)

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

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

4

13 回答 13

11

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

这里有几件重要的事情:

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

好的

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

坏的

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);
于 2010-07-07T19:34:21.773 回答
4

你试过改变

oWord.Visible = False

oWord.Visible = True

?

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

于 2010-03-25T16:15:34.790 回答
4

当我这样做时,我遇到了同样的问题:

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

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

于 2010-04-05T17:38:05.513 回答
2

我只做过 Excel 自动化,但遇到了类似的问题。引用一些旧代码,关闭的最后一步是 GC.Collect()

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

于 2010-03-24T21:35:03.020 回答
2

尝试调用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 问题的相关问题。

于 2010-03-25T15:54:51.663 回答
2

我发现使用自定义模板时使用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 时,模板页脚中的图像不会被复制到文档中。我将其作为一个单独的问题。但由于这种方法解决了我原来的问题,我'很满意。感谢所有提供答案的人!)

于 2010-03-26T18:08:29.233 回答
1

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

我用的是

Word.NormalTemplate.Saved = true;

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

我在这里的 Visual Basic 论坛上从用户“ NeedSomeAnswers ”那里得到了“NormalTemplate.Saved”提示。用他的话说“[it] 实际上并没有保存到 Normal,它只是告诉 Word,Normal 已经被保存,所以它不需要保存它”。

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

有一个美好的一天,并且很好。

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

于 2015-08-10T17:44:51.140 回答
0

“oDocuments”有 .Dispose() 或 .Close() 方法吗?您正在处理其他 2 个,但不是这个。

于 2010-03-24T21:18:00.703 回答
0

虽然这是 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 块中分配的cleaning up任何资源以及运行即使出现异常也必须执行的任何代码都很有用。不管 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
于 2010-03-24T21:40:13.493 回答
0

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

于 2010-03-25T19:38:53.043 回答
0
oWord.Visible = True

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

_wordApp.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;

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

于 2013-03-21T12:30:52.117 回答
0

这是一个完美的解决方案,我遇到了同样的问题,我只是按照这个解决方案,它运行完美。

对象 objFalse = 假;

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

于 2014-10-03T06:57:43.397 回答
0

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

希望这可以帮助。

于 2015-11-07T00:06:27.323 回答