Коллеги, сегодня мы рассмотрим весьма актуальную проблему, с которой часто сталкиваются разработчики 1С: очень долгое заполнение таблиц в шаблонах Word при использовании COM-объекта Word.Application в клиент-серверной конфигурации. Разберем, почему возникает эта задержка, и предложим современные, высокопроизводительные решения, которые позволят значительно сократить время формирования документов.
Автор нашей темы столкнулся с тем, что формирование печатной формы с заполнением таблицы в MS Word, реализованное через Внешнюю Печатную Форму (ВПФ), занимает неприемлемо много времени (до 40 минут для 67 строк) в клиент-серверном режиме, тогда как в файловой версии операция выполняется практически мгновенно. Мы проанализируем корневые причины такого поведения и представим пути решения.
Давайте сначала выясним, почему мы наблюдаем такую существенную разницу в производительности между файловой и клиент-серверной версиями 1С при работе с COM-объектом Word. Изначальный код, вызывающий проблему, выглядит следующим образом:
&НаКлиенте
Процедура Печать_ПриказОПремировании(СтруктураДок)
Word = Новый COMОбъект("Word.Application");
ИмяМакета="ЗаРезультатыГодовая";
Макет = ПолучитьМакетСервер(ИмяМакета);
// макет в формате ДД
ИмяФайла_Макет = ПолучитьИмяВременногоФайла("docx");
Макет.Записать(ИмяФайла_Макет);
ДокументWord = Word.Documents.ADD(ИмяФайла_Макет);
ДокументWord.Activate();
СоздатьИЗаполнитьТЗПодстановок_ПриказОПремировании(СтруктураДок);
Для Каждого СтрТЗ Из Объект.ПромТЗ Цикл
ПоискИЗаменаВWord_НаКлиенте(ДокументWord,СтрТЗ.Шаблон,СтрТЗ.СтрокаЗначения);
КонецЦикла;
Таб = ДокументWord.Tables.Item(2);
Для Сч = 1 По Объект.ПромТЗТаблица.Количество() Цикл
Таб.Rows.Add(Таб.Rows(2));
КонецЦикла;
Строка=2;
Для Каждого СтрТЗ Из Объект.ПромТЗТаблица Цикл
ДанныеСтроки=СтрТЗ;
//ВОТ ТУТ НАЧИНАЮТСЯ ТОРМОЗА В КЛИЕНТ-СЕРВЕРНОЙ ВЕРСИИ....
// --->
Таб.Cell(Строка,1).Range().InsertAfter(СтрТЗ.Подразделение);
Таб.Cell(Строка,2).Range().InsertAfter(СтрТЗ.Сотрудник);
Таб.Cell(Строка,3).Range().InsertAfter(СтрТЗ.Должность);
Таб.Cell(Строка,4).Range().InsertAfter(СтрТЗ.Премия);
// <----
Строка = Строка + 1;
КонецЦикла;
Таб.Rows(Строка).Delete();
Word.Visible = Истина;
Word.Application.WindowState = 2;//свернуть
Word.Application.WindowState = 1;//развернуть
Word.Activate();
КонецПроцедуры
Главная причина замедления в клиент-серверном режиме заключается в способе взаимодействия с COM-объектом Word:
&НаКлиенте), то все операции с объектом Word.Application, который физически запускается на клиентской машине, приводят к многочисленным сетевым вызовам. Каждое обращение к свойству или методу Word, например, Таб.Cell(Строка,1).Range().InsertAfter(), инициирует отдельный запрос по сети. При заполнении таблицы с большим количеством строк и ячеек это приводит к тысячам мелких сетевых пакетов, что катастрофически замедляет процесс. В файловом режиме все происходит локально на одном компьютере, поэтому задержек нет.
Word.Application предназначен для интерактивной работы пользователя. Программное управление им через COM, особенно выполнение большого числа мелких операций (построчное заполнение ячеек таблицы), не является оптимизированным для высокой производительности. Word выполняет множество внутренних операций, связанных с обработкой документа, его рендерингом и форматированием, даже если окно приложения невидимо.
Ctrl+C), этот текст может быть вставлен в формируемый документ Word, поскольку COM-объект может взаимодействовать с системным буфером обмена. Это может стать причиной серьезных ошибок и недоразумений.
Таким образом, основной "затык" происходит именно в цикле построчного заполнения ячеек таблицы, где каждое обращение к Таб.Cell(...).Range().InsertAfter() генерирует отдельное, медленное взаимодействие по сети.
Наиболее эффективным и современным решением данной проблемы является отказ от прямого использования COM-объекта Word на клиенте и переход к формированию документов на стороне сервера 1С с использованием механизмов работы с файлами формата OpenXML (.docx), предоставляемых Библиотекой Стандартных Подсистем (БСП).
Мы рассмотрим этот подход подробнее:
Вместо того чтобы запускать приложение Word на клиентской машине и управлять им через COM, мы можем формировать документ Word напрямую на сервере 1С. Современные файлы Word (.docx) по своей сути являются ZIP-архивами, содержащими набор XML-файлов, описывающих структуру и содержимое документа. Работая напрямую с этими XML-файлами, мы можем создавать и модифицировать документы значительно быстрее, так как нет необходимости запускать полноценное приложение Word.
Этот подход позволяет избежать всех проблем, связанных с сетевыми задержками и "тяжеловесностью" COM-объекта, поскольку вся работа по формированию документа происходит на сервере, где 1С-серверный процесс имеет прямой доступ к файлам и не требует взаимодействия с пользовательским интерфейсом.
Библиотека Стандартных Подсистем (БСП) в 1С:Предприятии предоставляет готовую функциональность для работы с внешними печатными формами и шаблонами документов, которая зачастую реализована именно через механизмы OpenXML. Это значительно упрощает разработку, поскольку не требуется писать низкоуровневый код для манипуляции XML-файлами.
При использовании БСП мы обычно работаем с шаблонами, в которых определены специальные области и поля для заполнения. В отличие от COM-объектов, где мы вставляем текст по ячейкам, механизмы БСП позволяют "раскручивать" области шаблона для таблиц или списков, передавая туда целые коллекции данных. Это значительно сокращает количество операций и, следовательно, время выполнения.
Наш автор темы подтвердил эффективность этого подхода, столкнувшись с первоначальными трудностями, но успешно освоив его. Он отметил, что настройка для существующих сложных макетов может быть трудоемкой на первом этапе, но при создании макетов с нуля или после освоения механизма, работа значительно ускоряется и становится более удобной.
Разберем основные принципы работы с таким подходом на примере, основанном на обсуждении в теме:
.docx, в котором определяем специальные области и поля для заполнения. Например, для списка или таблицы мы можем использовать синтаксис вида {v8 Область.ИмяОбласти} для обозначения повторяющихся блоков и {v8 Поле} для отдельных значений.
Пример структуры макета для списка, как показано в обсуждении:
{v8 Область.СписокУровень1}
1. {v8 ТекстСписок1}
{/v8 Область.СписокУровень1}
Для таблиц используются аналогичные принципы, где область может включать в себя строку таблицы, которая будет повторяться для каждой записи в передаваемой коллекции данных.
Например, чтобы покрасить часть текста или подчеркнуть его:
{v8 Область.СписокУровень1}
1. {v8 ТекстСписок1Часть} {v8 ТекстСписок1Часть_ЭтуКрасимЦветом}..... {v8 ТекстСписок1Часть_ЭтуПодчеркиваем}
{/v8 Область.СписокУровень1}
В этом случае мы передаем в каждое поле соответствующую часть текста, а в шаблоне Word заранее настраиваем форматирование для этих полей (например, задаем красный цвет для ТекстСписок1Часть_ЭтуКрасимЦветом и подчеркивание для ТекстСписок1Часть_ЭтуПодчеркиваем). Механизм БСП сохранит это форматирование при заполнении.
Таким образом, мы видим, что решение с использованием БСП и OpenXML является не только более производительным, но и более гибким и безопасным по сравнению с прямым COM-взаимодействием.
Если по каким-либо причинам вы все же вынуждены использовать COM-объект Word на клиенте, существуют некоторые общие рекомендации, которые могут немного улучшить производительность, хотя они не решают проблему сетевых задержек кардинально:
Word.Visible = Ложь до начала операций и делайте его видимым только в самом конце, если это необходимо. Это предотвращает ненужную перерисовку интерфейса, которая потребляет ресурсы. Однако, как мы выяснили, даже при невидимом окне Word может выполнять внутренние процессы, потребляющие время.
Пример:
Word.Visible = Ложь; // Отключаем видимость
// ... все операции с Word ...
Word.Visible = Истина; // Включаем видимость в конце
Пример:
Word.Application.ScreenUpdating = Ложь;
Word.Application.Options.CheckSpellingAsYouType = Ложь;
Word.Application.Options.CheckGrammarAsYouType = Ложь;
// ... операции ...
Word.Application.ScreenUpdating = Истина;
Word.Application.Options.CheckSpellingAsYouType = Истина;
Word.Application.Options.CheckGrammarAsYouType = Истина;
Важно не забыть включить эти опции обратно после завершения операций.
Например, можно сформировать HTML-код таблицы в строке 1С и затем вставить его в закладку Word:
// Предполагается, что данныеСформированыВHTML уже содержит полноценный HTML-код таблицы
// ДокументWord.Bookmarks("МояЗакладкаДляТаблицы").Range.InsertHtml(ДанныеСформированыВHTML);
Однако этот подход требует определенных навыков работы с HTML и может быть не всегда применим.
Мы проанализировали проблему медленного заполнения таблиц Word через COM-объект в клиент-серверной 1С и пришли к выводу, что наиболее эффективным и надежным решением является переход на современные механизмы работы с документами OpenXML через Библиотеку Стандартных Подсистем. Этот подход позволяет значительно увеличить скорость формирования документов за счет серверной обработки и прямого манипулирования XML-структурой файла, минуя "тяжеловесный" COM-объект Word.
Хотя начальное внедрение может показаться трудоемким, в долгосрочной перспективе это решение окупается стабильностью, производительностью и гибкостью, что было подтверждено нашим автором темы.