Delay when creating Microsoft.Office.Interop.Word.

2019-06-08 17:38发布

问题:

I've got an (legacy VB.Net) application that pulls data from some tables, populates a word template, and concatenates that template with several other files.

On several machines this works with no issues, but for one client there is a persistent problem where the Word Interop code throws Object reference not set to an instance of an object when attempting to open the template file (which exists, and has no permission issues, etc).

Dim doc As Document
Dim msWord As Microsoft.Office.Interop.Word.Application

msWord = New Microsoft.Office.Interop.Word.Application
' next line throws "Object reference not set to an instance of an object"
doc = msWord.Documents.Add(verifiedTemplateName)

When operating in a (horribly implemented) debug mode that throws up a bunch of modal dialogs that starts-n-stops execution, the exception is not thrown.

Dim doc As Document
Dim msWord As Microsoft.Office.Interop.Word.Application

msWord = New Microsoft.Office.Interop.Word.Application
MsgBox("VooDoo coder at work")
' now no exception is thrown
doc = msWord.Documents.Add(verifiedTemplateName)

When operation in normal mode, with a delay of some seconds, the exception is not thrown.

Dim doc As Document
Dim msWord As Microsoft.Office.Interop.Word.Application

msWord = New Microsoft.Office.Interop.Word.Application
Delay(5) ' function that pauses for one second
' now no exception is thrown
doc = msWord.Documents.Add(verifiedTemplateName)

This suggests that, on some machines, the Word.Application takes some time to "spin up."

But how best to trap for this, and continue once it exists; or throw an error if the time-frame is obscene (as always, best decided by the local jurisdiction)?

This is also an issue reported by others in the MSDN forums @ WordApplication.Documents.Add Method return null?

The only suggested solutions I've seen are potential infinite loops:

Document nulldoc = null;
do
{
    document = application.Documents.Add(template, newtemplate, documenttype, visible);
    Thread.Sleep(100);
}
while (document == nulldoc);

Is there a better solution to this than dumb delays, or possibly-infinite check-loops?

See Also: Error when creating an instance of Word in VB.net. Same error, similar code; but the solution was to ensure that the target file existed (which does, in my case).

回答1:

I remember solving problems with communicating with a busy out-proc COM server (Visual Studio in my case), by implementing the COM IMessageFilter interface (which is not the same as System.Windows.Forms.IMessageFilter).

There's an example of the technique in this MSDN article.

Since your problem happens when Word is busy starting up, it may be that this technique will help.

As an aside, when automating Office as you are doing, you are likely to come across the problem of Office failing to quit, as described in this KB article.

To solve this, you need to be careful to call Marshal.ReleaseComObject on every COM object you instantiate, preferably in a try/finally construct. To make sure you don't miss any references, avoid the "double-dot" construct msWord.Documents.Add, and instead explicitly create a reference to msWord.Documents.

Your code should look more like (I'm guessing the VB.NET syntax, but you'll get the idea):

Dim msWord As Application
Dim doc As Document
Dim docs As Documents

Try
    msWord = ...
    docs = msWord.Documents
    ' Test if docs is Nothing to avoid a NullReferenceException
    If Not docs Is Nothing Then
        doc = docs.Add...
        ...
    End If
    ...
Finally
    If Not doc Is Nothing Then Marshal.ReleaseComObject doc
    If Not docs Is Nothing Then Marshal.ReleaseComObject docs
    If Not msWord Is Nothing Then 
        msWord.Quit
        Marshal.ReleaseComObject msWord
    End If
End Try