При разработке внешних печатных форм и обработок для управляемого приложения (1С:Предприятие 8.3) разработчики часто сталкиваются со спецификой клиент-серверной архитектуры. Классический код для обычных форм, где макет Word типа ActiveDocument получался и открывался напрямую, в тонком клиенте вызывает критические ошибки.
Разберем типичные ошибки, проанализируем их причины и рассмотрим правильные варианты реализации печатных форм MS Word.
Рассмотрим две наиболее распространенные проблемы, с которыми сталкиваются при попытке сгенерировать документ Word:
Active document, и разработчик пытается вернуть объект макета из серверной функции на клиент (или поместить его в ВременноеХранилище). Тип ОболочкаActiveDocument не сериализуется через XDTO и не может передаваться по контексту между сервером и клиентом.COMОбъект("Word.Application") или ПолучитьCOMОбъект()) на сторону сервера приводит к сбою, если на сервере 1С не установлен офисный пакет Microsoft Office. На серверных машинах установка офисных приложений зачастую запрещена политиками безопасности или невозможна из-за отсутствия лицензий. Кроме того, создание COM-объектов на 64-разрядном сервере 1С требует 64-битных версий библиотек Office.Для гарантированной работы печатной формы в клиент-серверной базе без установки Microsoft Office на сервере разберем по шагам правильную цепочку действий:
.doc, .docx или .dot.ДвоичныеДанные и помещаем его во временное хранилище с привязкой к уникальному идентификатору текущей формы.ПолучитьИмяВременногоФайла().Посмотрим на пример правильной программной реализации в модуле формы обработки:
&НаКлиенте
Процедура СформироватьДоговор(Команда)
// Получаем адрес макета из временного хранилища на сервере
АдресХранилища = ПодготовитьМакетНаСервере();
Если Не ЗначениеЗаполнено(АдресХранилища) Тогда
Возврат;
КонецЕсли;
// Попытка инициализации COM-объекта на стороне клиента
Попытка
WordОбъект = Новый COMОбъект("Word.Application");
Исключение
Сообщить("Не удалось запустить Microsoft Word на клиентском компьютере: " + ОписаниеОшибки(), СтатусСообщения.Внимание);
Возврат;
КонецПопытки;
// Извлекаем двоичные данные макета и сохраняем во временный файл на клиенте
ДвоичныеДанныеМакета = ПолучитьИзВременногоХранилища(АдресХранилища);
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("docx");
ДвоичныеДанныеМакета.Записать(ИмяВременногоФайла);
Попытка
// Открываем документ на основе сохраненного файла шаблона
ДокументWord = WordОбъект.Documents.Add(ИмяВременногоФайла);
// Выполняем замену текстовых маркеров/параметров
ПоискИЗамена = ДокументWord.Content.Find;
ПоискИЗамена.Execute("<НомерДоговора>", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, "123/2024", 2);
ПоискИЗамена.Execute("<Контрагент>", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, "ООО ""Торговый Дом""", 2);
// Отображаем заполненный документ пользователю
WordОбъект.Application.Visible = Истина;
WordОбъект.Activate();
Исключение
WordОбъект.Quit();
WordОбъект = Неопределено;
УдалитьФайлы(ИмяВременногоФайла);
ВызватьИсключение "Ошибка при заполнении шаблона Word: " + ОписаниеОшибки();
КонецПопытки;
// Удаляем временный файл шаблона после открытия в Word
УдалитьФайлы(ИмяВременногоФайла);
КонецПроцедуры
&НаСервере
Функция ПодготовитьМакетНаСервере()
// Получаем объект внешней обработки или конфигурации
ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
// Макет в дереве метаданных должен иметь тип "Двоичные данные"
ДвоичныеДанныеМакета = ОбработкаОбъект.ПолучитьМакет("МакетДоговора");
// Привязываем адрес к уникальному идентификатору формы, чтобы данные очистились при закрытии
АдресХранилища = ПоместитьВоВременноеХранилище(ДвоичныеДанныеМакета, УникальныйИдентификатор);
Возврат АдресХранилища;
КонецФункции
Рассмотрим детали, которые помогут избежать утечек памяти и нестабильной работы:
ПоместитьВоВременноеХранилище() вторым параметром всегда передавайте УникальныйИдентификатор текущей управляемой формы (а не создавайте Новый УникальныйИдентификатор()). Это гарантирует автоматическое освобождение памяти на сервере после закрытия окна формы.Попытка ... Исключение, вызывая WordОбъект.Quit() и обнуляя переменную WordОбъект = Неопределено при возникновении сбоя.WordОбъект.ScreenUpdating = Ложь, а параметр Visible = Истина включайте строго в самом конце.Разберем варианты, когда COM-объект не подходит или нежелателен в архитектуре:
.docx представляет собой ZIP-архив с файлами разметки XML. С помощью встроенных объектов 1С ЧтениеZipФайла, ЗаписьZipФайла и ЧтениеXML можно распаковать шаблон на сервере, заменить теги внутри файла word/document.xml и запаковать обратно. Этот метод работает на любой операционной системе (включая серверный Linux) и не требует установленного Microsoft Office.ТабличныйДокумент.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.DOCX).