При реализации механизма групповой печати через внешние печатные формы (ВПФ) программисты часто сталкиваются с ситуацией, когда код 1С отрабатывает быстрее, чем внешнее приложение (например, Microsoft Word) успевает создать и сохранить документ. В результате возникает «рассинхрон»: сообщения в логе перемешиваются, а последующие действия с файлами (например, их отправка по почте или упаковка в архив) завершаются ошибкой, так как файлы еще «заняты» или не созданы. Рассмотрим, почему это происходит и как правильно организовать ожидание завершения всех заданий.
Разберем ситуацию на программном уровне. Когда мы вызываем клиентский метод Печать внешней обработки, внутри которого происходит обращение к COM-объекту Word.Application, 1С отправляет команду операционной системе на запуск процесса или взаимодействие с ним. Однако Word является внешним по отношению к 1С приложением. Код 1С внутри цикла Для каждого выполняется синхронно в контексте платформы, но команды COM-серверу могут помещаться в очередь или выполняться в отдельном потоке Word. Программа 1С не «ждет» физического появления файла на диске или закрытия окна Word, если это явно не прописано в коде, и переходит к следующей итерации цикла.
Проанализируем пример кода, который вызывает подобные сложности:
Для каждого стр из СписокОтчетов цикл
Если стр.печатать тогда
// Подключение ВПФ и получение формы
ИмяОбработки = ПодключитьВнешнююОбработку(АдресХранилища);
ФормаОтчета = ПолучитьФорму("ВнешняяОбработка." + ИмяОбработки + ".Форма");
// Вызов клиентского метода печати
ФормаОтчета.Печать("", ОбъектыНазначения);
// В этот момент 1С считает, что задача выполнена,
// но Word может только начинать открывать шаблон
КонецЕсли;
Конеццикла;
Выясним причину странного поведения сообщений Сообщить. Из-за высокой скорости работы интерпретатора 1С и задержек в инициализации COM-объектов, сообщения «Старт» и «Стоп» могут выводиться раньше, чем визуально откроется окно Word. Если в коде ВПФ используются асинхронные вызовы или обработчики ожидания, ситуация усугубляется еще сильнее.
Если ваша задача — дождаться момента, когда все файлы будут сформированы для дальнейшей обработки (например, для сбора в один архив), самым простым, но действенным методом является цикл проверки доступности файла. Файл, который формируется Word-ом, обычно заблокирован операционной системой до момента окончания записи или закрытия потока.
Рассмотрим алгоритм реализации такой проверки:
Печать добавьте процедуру ожидания.Пример реализации функции ожидания:
Функция ФайлГотовКРаботе(ПолноеИмяФайла)
ФайлНаДиске = Новый Файл(ПолноеИмяФайла);
Если Не ФайлНаДиске.Существует() Тогда
Возврат Ложь;
КонецЕсли;
Попытка
// Пытаемся переименовать файл сам в себя -
// это самый быстрый способ проверить блокировку ОС
ПереместитьФайл(ПолноеИмяФайла, ПолноеИмяФайла);
Возврат Истина;
Исключение
Возврат Ложь;
КонецПопытки;
КонецФункции
Используйте эту функцию с небольшим ожиданием через ПодключитьОбработчикОжидания, чтобы не «подвешивать» интерфейс пользователя бесконечным циклом.
Если у вас есть возможность модифицировать код внутри ВПФ или получить ссылку на объект Word.Application, мы можем проанализировать внутреннее состояние принтера Word. У объекта Word есть свойство BackgroundPrintingStatus, которое показывает количество заданий в очереди фоновой печати.
Разберем, как это применить. Пока значение BackgroundPrintingStatus больше нуля, Word продолжает обработку документа. Проанализируем пример кода ожидания:
// Предположим, MSWord - это ссылка на COM-объект Word.Application
Пока MSWord.BackgroundPrintingStatus > 0 Цикл
// Ждем завершения фоновых процессов Word
#Если Клиент Тогда
ОбработкаПрерыванияПользователя();
#КонецЕсли
КонецЦикла;
Важно помнить, что этот метод работает только в том случае, если вы сохраняете ссылку на COM-объект в течение всего процесса печати. Если форма ВПФ закрывается и объект уничтожается, отследить состояние будет невозможно.
Чтобы избежать «каши» и обеспечить последовательное выполнение, мы можем отказаться от цикла Для каждого в пользу рекурсивного вызова через очередь. Это гарантирует, что следующая форма начнет печататься только после того, как мы получим подтверждение от предыдущей.
Посмотрим на шаги реализации:
ОчередьПечати.НапечататьСледующийИзОчереди().Печать.ПодключитьОбработчикОжидания("НапечататьСледующийИзОчереди", 1, Истина).Такой подход дает Word-у необходимую «паузу» в 1 секунду между инициализациями процессов, что обычно достаточно для стабильной работы без перемешивания потоков сообщений.
Если проект позволяет отойти от использования клиентских методов и COM-соединений, крайне рекомендуем перевести формирование документов Word на серверную сторону. Современные форматы .docx являются обычными zip-архивами с xml внутри. Библиотеки для работы с ними позволяют формировать документы без установленного Microsoft Word на сервере.
Преимущества этого метода:
В этом случае задача «как узнать, что все выполнено» исчезает сама собой: как только цикл на сервере завершился, вы гарантированно имеете массив двоичных данных всех документов.
Несмотря на споры о сложности Библиотеки Стандартных Подсистем (БСП), ее механизмы печати (подсистема «Печать») спроектированы для решения именно этих проблем. БСП использует длительные операции. Когда вы запускаете печать комплекта, система создает фоновое задание на сервере, а клиентская часть периодически опрашивает статус этого задания.
Если вы решите использовать БСП, обратите внимание на метод УправлениеПечатьюКлиент.ВыполнитьКомандуПечати. Он берет на себя всю рутину по подключению обработок, вызову методов и, что немаловажно, корректному завершению процессов. Для групповой печати в БСП предусмотрен механизм формирования единого табличного документа или коллекции файлов, что исключает возникновение «каши» в пользовательском интерфейсе.
Выясним итог: для решения вашей проблемы в текущем коде наиболее эффективным будет внедрение очереди печати через обработчик ожидания или цикл проверки занятости файла. Это позволит синхронизировать быстрый код 1С с медленным процессом формирования документов в Microsoft Word.