Dot-Net

呼叫 Word.Documents.Add 後 WinWord.exe 不會退出 - Word .NET Interop

  • March 24, 2010

我遇到了經典場景,在 .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(),那麼問題就不會發生。

我發現使用自定義模板時使用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 時,模板頁腳中的圖像不會被複製到文件中。我將其作為一個單獨的問題。但由於這種方法解決了我原來的問題,我’很滿意。感謝所有提供答案的人!)

(我所有的建議都改編自這個關於 Excel 互操作的答案。)

這裡有幾件重要的事情:

  1. 切勿在同一行上使用 2 個點。還將索引器視為一個點

好的

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

壞的

Word.Document aDoc = wordApp.Documents.Open(/*...*/);
  1. 釋放所有指針。

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

引用自:https://stackoverflow.com/questions/2511464