При формировании печатных форм договоров и других документов в Microsoft Word средствами OLE Automation из платформы 1С 7.7 разработчики нередко сталкиваются с ситуацией, когда замена текста в теле документа отрабатывает корректно, а попытка изменить колонтитулы приводит к ошибкам времени выполнения. Разберем типичные ошибки, проанализируем причины их возникновения и рассмотрим правильные варианты реализации задачи.
Посмотрим на классический сценарий. Для замены текста в основном теле документа часто используется объект выделения Word.Selection:
Word.Selection.Find.Replacement.ClearFormatting();
Word.Selection.Find.Execute(Текст1, 0, 0, 0, 0, 0, 1, 1, 0, Текст2, 2);
Однако при попытке обратиться к колонтитулу через Word.Selection(1).Footers... платформа 1С 7.7 возвращает ошибку «Коллекции не поддерживаются». Выясним причину такого поведения. Механизм COM/OLE Automation в 1С 7.7 не умеет индексировать произвольные OLE-объекты как массивы, если они не являются полноправными COM-коллекциями. Сам объект Selection коллекцией не является. Кроме того, поиск через Word.Selection физически ограничен областью основного текста документа (MainDocument) и не затрагивает колонтитулы.
Следующая частая попытка исправления — вызов коллекции разделов через корневой объект приложения: Word.Sections.Item(1).... В этом случае возникает ошибка «Поле агрегатного объекта не обнаружено (Sections)». Причина проста: коллекция Sections принадлежит конкретному документу (объекту Document), а не всему приложению Word.Application.
Также ошибку «Microsoft Word: "Range" не является методом» или «Неопознанный оператор» вызывает попытка обращаться к свойству Range со скобками или использовать его в отдельной строке без присваивания или вызова методов.
Чтобы получить доступ к колонтитулу, необходимо обращаться строго к объекту открытого документа (переменной, возвращенной методом Documents.Add() или Documents.Open()), последовательно вызывая нужный раздел из коллекции Sections и нужный колонтитул из коллекции Footers.
Разберем по шагам готовый код процедуры замены текста в нижнем колонтитуле:
Процедура ЗаменитьТекстКолонтитула(Doc, Текст1, Текст2)
мКолонтитул = Doc.Sections(1).Footers(1).Range;
мКолонтитул.Find.ClearFormatting();
мКолонтитул.Find.Replacement.ClearFormatting();
мКолонтитул.Find.Execute(Текст1, 0, 0, 0, 0, 0, 1, 1, 0, Текст2, 2);
КонецПроцедуры
Рассмотрим подробнее вызов основного метода печати документа:
Процедура Печать()
ШаблонДоговора = КаталогИБ() + "Шаблоны\Договор.doc";
Если ФС.СуществуетФайл(ШаблонДоговора) = 0 Тогда
Предупреждение("Файл шаблона не найден!");
Возврат;
КонецЕсли;
Word = СоздатьОбъект("Word.Application");
Word.Visible = 1;
Doc = Word.Documents.Add(ШаблонДоговора);
// Замена в основном тексте документа
Word.Selection.Find.Execute("%НомерДоговора%",,,,,,,,,СокрЛП(НомерДог), 2);
// Замена в нижнем колонтитуле первого раздела
ЗаменитьТекстКолонтитула(Doc, "%НомерДоговора%", СокрЛП(НомерДог));
ЗаменитьТекстКолонтитула(Doc, "%ДатаДоговора%", СокрЛП(Строка(ДатаДог)));
Doc.Activate();
Word = 0;
КонецПроцедуры
Проанализируем ситуации, когда стандартного обращения к первому элементу Footers(1) может быть недостаточно:
Footers содержит три элемента:
1 (wdHeaderFooterPrimary) — основной колонтитул документа.2 (wdHeaderFooterFirstPage) — колонтитул первой страницы.3 (wdHeaderFooterEvenPages) — колонтитул для четных страниц.Footers(1) заменит текст только со второй страницы. Для первой страницы замену нужно продублировать в Footers(2).
Doc.Sections(1) изменит текст исключительно в первом разделе. В 1С 7.7 отсутствует цикл Для Каждого, поэтому обход всех секций организуется с помощью обычного цикла по счетчику:
Для НомСекции = 1 По Doc.Sections.Count Цикл
Doc.Sections(НомСекции).Footers(1).Range.Find.Execute(Текст1,,,,,,,,,Текст2, 2);
КонецЦикла;
Посмотрим на другие варианты реализации, если поиск по тексту дает сбои или шаблон содержит сложную структуру:
Word.Selection:
Word.ActiveWindow.ActivePane.View.SeekView = 10; // 10 = wdSeekCurrentPageFooter
Word.Selection.Find.Execute(Текст1,,,,,,,,,Текст2, 2);
Word.ActiveWindow.ActivePane.View.SeekView = 0; // 0 = wdSeekMainDocument (возврат в тело)
Если Doc.Bookmarks.Exists("НомерДоговора") = 1 Тогда
Doc.Bookmarks("НомерДоговора").Range.Text = СокрЛП(НомерДог);
КонецЕсли;
Обратим внимание на завершение работы с OLE: простое присвоение Word = 0 лишь разрывает связь переменной 1С с процессом. Если документ генерируется в скрытом режиме (Word.Visible = 0) для фоновой печати или сохранения в файл, обязательно закрывайте документ и само приложение явным вызовом команд Doc.Close(0) и Word.Quit() во избежание накопления зависших процессов WINWORD.EXE в диспетчере задач.