Задержка при создании Microsoft.Office.Interop.Word.Application

У меня есть (устаревшее VB.Net) приложение, которое извлекает данные из некоторых таблиц, заполняет шаблон слова и объединяет этот шаблон с несколькими другими файлами.

На нескольких машинах это работает без проблем, но для одного клиента существует постоянная проблема, когда код Word Interop выдает Object reference not set to an instance of an object при попытке открыть файл шаблона (который существует и не имеет проблем с разрешениями и т. д.).

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)

При работе в (ужасно реализованном) режиме отладки, который вызывает кучу модальных диалогов, которые запускают и останавливают выполнение, исключение не выдается.

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)

При работе в обычном режиме с задержкой в ​​несколько секунд исключение не выбрасывается.

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)

Это говорит о том, что на некоторых машинах приложению Word.Application требуется некоторое время, чтобы «раскрутиться».

Но как лучше поймать это и продолжить, когда оно существует; или выдать ошибку, если сроки непристойны (как всегда, лучше решать в местной юрисдикции)?

Об этой проблеме также сообщают другие участники форумов MSDN @ Метод WordApplication.Documents.Add возвращает значение null?

Единственные предлагаемые решения, которые я видел, - это потенциальные бесконечные циклы:

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

Есть ли лучшее решение этой проблемы, чем глупые задержки или, возможно, бесконечные циклы проверки?

См. также: Ошибка при создании экземпляра Word в VB .net. Та же ошибка, аналогичный код; но решение заключалось в том, чтобы убедиться, что целевой файл существует (в моем случае это действительно так).


person Michael Paulukonis    schedule 25.10.2011    source источник


Ответы (1)


Я помню, как решал проблемы со связью с загруженным внешним COM-сервером (Visual Studio в моем случае) путем реализации интерфейс COM IMessageFilter (который не совпадает с System.Windows.Forms.IMessageFilter).

Пример этой техники приведен в этой статье MSDN.

Поскольку ваша проблема возникает, когда Word занят запуском, может помочь этот метод.

Кроме того, при автоматизации Office, как вы это делаете, вы, вероятно, столкнетесь с проблемой невозможности выхода из Office, как описано в этой статье базы знаний.

Чтобы решить эту проблему, вам нужно с осторожностью вызывать Marshal.ReleaseComObject для каждого создаваемого вами COM-объекта, предпочтительно в конструкции try/finally. Чтобы убедиться, что вы не пропустите ни одной ссылки, избегайте конструкции "двойная точка" msWord.Documents.Add и вместо этого явно создавайте ссылку на msWord.Documents.

Ваш код должен выглядеть примерно так (я предполагаю синтаксис VB.NET, но вы поймете идею):

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
person Joe    schedule 25.10.2011
comment
Благодарю. На самом деле я использую Marshal.FinalReleaseComObject(), как подробно описано @ blogs.msdn.com/b/yvesdolc/archive/2004/04/17/115379.aspx - person Michael Paulukonis; 25.10.2011