Странное исключение при попытке автоматизировать MS Word из vb.net

#vb.net #ms-word

#vb.net #ms-word

Вопрос:

Я использую следующее vb.net функция для чистого сохранения документов Word (никакого ввода текста вообще, на данный момент меня интересует только массовое создание пустых документов word):

 Sub createDoc(ByVal cname As String, ByVal acctype As String)
    counter  = 1
    wordDoc = wordApp.Documents.Add
    wordDoc.SaveAs(OFDD.SelectedPath amp; "" amp; cname amp; "_" amp; acctype amp; ".docx")
    wordDoc.Close()
End Sub
  

Переменная OFDD — это имя компонента vb браузера папок, а ее свойство SelectedPath в сочетании с параметрами cname и acctype предоставляют мне имя документа Word, который я хочу создать и сохранить. Вот объявления переменных counter, wordDoc и WordApp:

  Private Shared counter As Integer = 0
 Private wordApp As New Word.Application
 Private wordDoc As Word.Document
  

Переменная wordDoc присваивается объекту Document с помощью второй строки кода в подпрограмме createDoc. Однако, похоже, что в 83-й раз, когда я пытаюсь извлечь объект document и назначить его wordDoc, я получаю исключение, в котором говорится, что «Команда не выполнена» . Я могу сказать, что это 83-й раз, когда я ввожу функцию, потому что в моем catchblock я печатаю значение counter в окне сообщения, сразу после печати сведений о полученном исключении и прямо перед тем, как я освобождаю свои использованные ресурсы и завершаю процесс.

Обеспокоенный тем, есть ли в моей системе ограничения, связанные с автоматизацией MS Word, я создал другой проект Visual Studio (на этот раз консольный проект), сославшись на Microsoft.Взаимодействие.Офис.Пространство имен Word и написал следующий простой модуль:

 Imports Word = Microsoft.Office.Interop.Word
Module Module1

Sub Main()
    Dim wordApp As New Word.Application
    Try
        For i As Integer = 0 To 150
            Dim document As Word.Document = wordApp.Documents.Add()
            document.SaveAs("C:WordTester" amp; i amp; ".docx")
            document.Close()
        Next
    Catch
        wordApp.Quit()
    End Try
    Console.WriteLine("Document objects left in memory: " amp; _ 
                       wordApp.Documents.Count) ' zero
    Console.Read()
    wordApp.Quit()
End Sub

End Module
  

Который работает отлично. Проверяя свою файловую систему, я вижу 150 файлов word, созданных в «C:WordTester «. Учитывая все эти мои усилия, я действительно озадачен тем, почему первый написанный мной код застревает на 83-й попытке создать и сохранить документ, и любая помощь была бы безмерно признательна.

Спасибо за ваше время,

Джейсон

Редактировать: Вот отредактированная версия createDoc, на которую я ссылаюсь в комментарии ниже:

 Sub createDoc(ByVal cname As String, ByVal acctype As String)
    counter  = 1
    wordApp = New Word.Application
    wordDoc = New Word.Document
    wordDoc = wordApp.Documents.Add
    wordDoc.SaveAs(OFDD.SelectedPath amp; "" amp; cname amp; "_" amp; acctype amp; ".docx")
    wordDoc.Close()
    wordApp.Quit()
End Sub
  

Ответ №1:

Попробуйте что-то вроде этого. Очевидно, мне пришлось вставить немного предполагаемого кода, но это в значительной степени то, что вы должны делать:

 Imports Word = Microsoft.Office.Interop.Word

Public Class Class1

Private pWordApp As Word.Application
Private pintCounter As Integer = 0

Public Sub CreateWordDocuments()

    '--instanciate word:
    pWordApp = New Word.Application

    Dim dt As DataTable = GetYourDataEtc    'replace with however you get the data you loop around
    Dim OFDD As Object = GetYourFolderPathEtc   'replace Object and folder path call and 

    Try

        For Each dr In dt.Rows
            Dim cName As String = dr("cname")  'for example
            Dim acctype As String = dr("acctype") #for example

            CreateDoc(OFDD, cName, acctype)
        Next

    Catch ex As Exception
        '--some error code
    Finally
        pWordApp.Quit()
        System.Runtime.InteropServices.Marshal.ReleaseComObject(pWordApp)
    End Try

End Sub

Private Sub CreateDoc(ByVal OFDD As Object, ByVal cname As String, ByVal accType As String)

    Dim document As Word.Document = pWordApp.Documents.Add()
    document.SaveAs(OFDD.SelectedPath amp; "" amp; cname amp; "_" amp; accType amp; ".docx")
    document.Close()

End Sub


End Class
  

Комментарии:

1. Да, это всегда происходит в 83-й раз. Мои входные данные взяты из запроса базы данных SQL Server, содержащего около 250 записей, поэтому я проверил, могут ли входные данные в строке 83 содержать поля NULL; это не так. Мой существующий блок catch выводит следующее окно сообщения: MsgBox("Error : " amp; Err.Description amp; " from method: " amp; Err.Source amp; " in line " amp; Err.Erl amp; ".", vbCritical) MsgBox("Document Objects in memory: " amp; wordApp.Documents.Count) MsgBox("Counter variable value : " amp; counter) которое, я думаю, было бы достаточно вербальным, возможно?

2. Я имел в виду поместить другой блок try catch в фактическую процедуру CreateDoc и прервать работу над ошибкой. Затем выполните interogate с объектом word, чтобы увидеть, что происходит.

3. Мальчик Буджи прав в том, что он говорит. Однако ваша отредактированная процедура CreateDoc не является хорошей идеей, поскольку вы запускаете и закрываете word для каждого документа. Почему бы просто не скопировать рабочий консольный код в класс. Создайте ссылку на word вверху, обработайте каждый документ закрытым word и утилизируйте?

Ответ №2:

Похоже на проблему со сборкой мусора — среда CLR содержит множество ссылок на объект word, и у word заканчивается какой-либо ресурс до запуска сборки мусора. Вторая функция работает, потому что все ограничено локально. Вы можете изменить код, чтобы охватывать объекты локально; также поможет вызов dispose для объекта worddoc.

Похоже, что нет dispose. Правильный способ — вызвать метод ReleaseComObject

http://msdn.microsoft.com/en-us/library/aa159887 (office.11).aspx

Комментарии:

1. Хм, боюсь, я не совсем понимаю, что вы подразумеваете под «локальным охватом». На что мне следует изменить характер моего кода, чтобы убедиться, что сборка мусора ничего не испортит?

2. Кроме того, что касается вызова «Dispose ()» для объекта worddoc, я получаю исключение, в котором говорится, что «Общедоступный элемент Dispose() для типа «DocumentClass» не найден».

3. и wordapp, и word doc объявлены в вложенной (локальной области), поэтому оба попадают в кучу вместо стека. Стек будет очищен после завершения работы sub. Первый код содержит wordapp и worddoc, объявленные в модуле и, следовательно, (глобальная область видимости), и в конечном итоге они окажутся в куче, и сборщику мусора потребуется больше времени для очистки

4. Однако разве это не было бы тем, чего я хочу? Я имею в виду, я хочу, чтобы объекты были очищены как можно позже, чтобы мои объекты wordDoc и WordApp не начали вести себя неправильно, так же, как сейчас. Следует также отметить, что иерархия первого кода выглядит следующим образом: class-> sub main -> central для цикла, который вызывает sub createDoc(), если выполняются некоторые требования.

5. Да, объявлять все локально будет медленнее, но это поможет с очисткой от мусора. что-то столь же простое, как изменение объявления wordapp на Dim WordApp в качестве Word. Приложение и создайте экземпляр WordApp = Новое слово. Приложение в конструкторе может помочь. также установите worddoc = ничего после вызова close

Ответ №3:

Вместо того, чтобы создавать новый экземпляр Word каждый раз, когда вы создаете документ, я бы предпочел создать ОДИН экземпляр перед циклом для создания одного документа и использовать этот объект приложения во время цикла — это подход, предложенный dunc.

ПРИМЕЧАНИЕ: После цикла вы освобождаете COM-объект с помощью Marshal.ReleaseComObject. Я бы вызвал эту функцию также в подпрограмме CreateDoc для объекта doc. Пожалуйста, взгляните на это обсуждение о выпуске COM-объектов в Office automation.

Ответ №4:

Изучив его большую часть рабочего дня, я, наконец, обнаружил, что проблема вообще не заключалась в процессе сборки мусора. Я также не создавал слишком много объектов document. Просто оказывается, что мой ввод в базу данных содержал записи с двойными кавычками, а MS Word не допускает двойных кавычек в именах файлов.

Спасибо за ваше время и интерес.