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

时间:2010-03-24 21:15:28

标签: .net com interop ms-word office-interop

我遇到了经典场景,当在.NET中创建Word COM对象时(通过Microsoft.Office.Interop.Word程序集),即使我是{{3},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(),那么问题就不会发生。

13 个答案:

答案 0 :(得分:11)

(我的所有建议都改编自this answer关于Excel互操作。)

这里有一些重要的事情:

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

不可

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

答案 1 :(得分:4)

您是否尝试过更改

oWord.Visible = False

oWord.Visible = True

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

答案 2 :(得分:3)

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

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

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

答案 3 :(得分:2)

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

本文也提到: http://support.microsoft.com/kb/317109

答案 4 :(得分: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)

您可能还想查看this related question讨论Excel的问题。

答案 5 :(得分:2)

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

所以我换了:

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

with:

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

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

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

答案 6 :(得分:1)

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

我使用的是

Word.NormalTemplate.Saved = true;

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

我从视觉基础论坛here上的用户“ NeedSomeAnswers ”获得了“NormalTemplate.Saved”提示。在他的话中,“[它]实际上并没有保存到Normal,它只是告诉Word,Normal已经被保存,所以它不需要保存它”。

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

度过一个美好的一天,并且做得好。

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

答案 7 :(得分:0)

“oDocuments”是否有.Dispose()或.Close()方法?你正在处理另外2个,但不是这个。

答案 8 :(得分: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 块对于cleaning up在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

答案 9 :(得分:0)

You should not discard the document object created in Word.Documents.Add。保存并在完成后从自动化获得的每个COM对象上调用Marshal.ReleaseComObject,即if you do not cache the objects anywhere

答案 10 :(得分:0)

oWord.Visible = True

解决了我的问题。根本问题是文件恢复。尽管有一条线,但仍出现了一个对话框:

_wordApp.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;

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

答案 11 :(得分:0)

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

object objFalse = false;

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

答案 12 :(得分:0)

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

希望这有帮助。