При переходе с файловой базы данных 1С на клиент-серверный вариант работы разработчики часто сталкиваются с неприятной ситуацией: код, который годами успешно формировал печатные формы на основе макетов Microsoft Word (технология Active Document), внезапно перестает работать. На серверной стороне вызов метода Получить() для макета завершается аварийно, выдавая системную ошибку.
В этой статье мы подробно проанализируем, почему возникает данная проблема, рассмотрим внутренние ограничения платформы 1С:Предприятие и разберем три практических метода решения этой задачи — от рекомендуемого стандарта до тонкой настройки операционной системы Windows Server. Перед выполнением подобных технических работ на сервере рекомендуется провести автоматическое архивирование и тестирование всех баз 1С.
Давайте выясним первопричину сбоя. В исходном коде, запущенном в контексте &НаСервере, разработчики обычно пытаются выполнить следующую последовательность действий:
СсылкаМакет = ОбработкаОбъект.ПолучитьМакет("NNN");
Word = СсылкаМакет.Получить(); // Здесь происходит падение платформы
Эта конструкция содержит сразу три критические архитектурные проблемы:
Получить() у объекта типа ОболочкаActiveDocument имеет жесткое ограничение в синтакс-помощнике платформы 1С: Доступность: Толстый клиент. Попытка выполнить этот метод в тонком клиенте или на стороне сервера (где контекст исполнения принципиально иной) гарантированно вызовет исключение «Метод объекта не обнаружен».ОболочкаActiveDocument не может быть передан через границу «сервер-клиент». Если вы попытаетесь вернуть полученный макет из серверной функции на клиент, платформа выдаст ошибку преобразования XDTO.USR1CV8) работает в изолированной системной сессии Windows, не имеющей доступа к графическому интерфейсу пользователя (так называемая Session 0). Когда код на сервере пытается инициализировать COM-объект Word.Application и сделать его видимым с помощью свойства Visible = Истина, операционная система блокирует это действие, что приводит к зависанию или аварийному завершению процесса.Посмотрим, какими путями мы можем решить эту задачу в современных реалиях управляемого приложения. Если задачу формирования документов необходимо решить без написания программного кода, используется платформа для создания программных роботов One RPA.
Использование макетов типа Active Document в современных конфигурациях на управляемых формах считается устаревшим и крайне нестабильным подходом. Вместо этого мы рекомендуем перевести макет в категорию Двоичные данные. Это полностью решает проблему совместимости, избавляет от необходимости устанавливать Microsoft Office на сервер 1С и позволяет генерировать документы силами локального компьютера пользователя.
Рассмотрим пошагово алгоритм реализации этого метода:
.docx или .doc).{ФИОПолучателя}, {НомерДоговора}) или стандартные закладки (Bookmarks).Проанализируем пример программного кода, реализующего этот надежный подход:
Код на стороне сервера:
&НаСервере
Функция ПолучитьМакетВоВременномХранилище()
// Извлекаем двоичные данные файла Word из макета конфигурации
ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
МакетДвоичныеДанные = ОбработкаОбъект.ПолучитьМакет("ДоговорШаблон");
// Помещаем двоичные данные во временное хранилище сеанса
АдресХранилища = ПоместитьВоВременноеХранилище(МакетДвоичныеДанные, УникальныйИдентификатор);
Возврат АдресХранилища;
КонецФункции
Код на стороне клиента:
&НаКлиенте
Процедура СформироватьДокументНаКлиенте(Команда)
// Шаг 1. Получаем адрес файла из временного хранилища сервера
АдресХранилища = ПолучитьМакетВоВременномХранилище();
Если ПустаяСтрока(АдресХранилища) Тогда
ВызватьИсключение "Не удалось получить шаблон документа с сервера.";
КонецЕсли;
// Шаг 2. Извлекаем двоичные данные и записываем во временный файл на локальном диске
ДвоичныеДанныеМакет = ПолучитьИзВременногоХранилища(АдресХранилища);
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(".docx");
ДвоичныеДанныеМакет.Записать(ИмяВременногоФайла);
// Шаг 3. Инициализируем локальный COM-объект Microsoft Word
Попытка
WordApp = Новый COMОбъект("Word.Application");
Исключение
ПоказатьПредупреждение(, "Не удалось запустить Microsoft Word. Убедитесь, что приложение установлено локально.");
Возврат;
КонецПопытки;
// Шаг 4. Открываем временный файл-шаблон в приложении Word
Попытка
WordDoc = WordApp.Documents.Add(ИмяВременногоФайла);
WordApp.Visible = Истина;
// Шаг 5. Производим заполнение параметров методом контекстного поиска и автозамены
Замена = WordDoc.Content.Find;
// Заменяем маркер номера договора
Замена.Execute("{НомерДоговора}", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, "123-АП", 2);
// Заменяем маркер даты договора
Замена.Execute("{ДатаДоговора}", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, Формат(ТекущаяДата(), "ДЛФ=DD"), 2);
// Активируем окно текстового редактора для пользователя
WordApp.Activate();
Исключение
// В случае непредвиденной ошибки обязательно закрываем процесс Word во избежание утечки памяти
Если Не WordApp = Неопределено Тогда
WordApp.Quit(Ложь);
КонецЕсли;
ВызватьИсключение "Произошла ошибка при обработке документа Word: " + ОписаниеОшибки();
КонецПопытки;
КонецПроцедуры
Этот способ полностью исключает конфликты безопасности ОС на сервере, избавляет от необходимости покупать лицензии MS Office для сервера и гарантирует стабильную работу решения даже при работе пользователей через Веб-клиент в веб-браузерах.
Бывают ситуации, когда формирование документов Word должно происходить в фоновом режиме без участия пользователя (например, регламентными заданиями по расписанию для последующей отправки счетов клиентам по электронной почте). В этом случае перенести выполнение на клиентскую сторону невозможно, и нам придется научить серверную службу 1С корректно работать с COM-объектом Microsoft Word.
Для реализации этого сценария системному администратору потребуется выполнить тонкую настройку операционной системы Windows Server, на которой развернут сервер приложений 1С:
Процессы Microsoft Office при инициализации через COM-интерфейс в обязательном порядке проверяют физическое наличие папки Desktop в профиле пользователя, запустившего сеанс. У системной учетной записи службы 1С (чаще всего это USR1CV8) такой папки по умолчанию нет. Вам необходимо создать её вручную:
C:\Windows\SysWOW64\config\systemprofile\DesktopC:\Windows\System32\config\systemprofile\DesktopВажно: После создания папки зайдите в её свойства безопасности и предоставьте учетной записи службы сервера 1С (USR1CV8 или LOCAL SERVICE) полные права доступа (Чтение, Запись, Изменение).
Для того чтобы служба сервера имела право запускать COM-объект текстового процессора, настроим разрешения распределенной объектной модели DCOM:
dcomcnfg и нажмите клавишу ввода. (Внимание: если сервер приложений 1С имеет разрядность x64, а установленный пакет Microsoft Office — x86, запустите консоль DCOM специальной командой: comexp.msc /32).USR1CV8), наделив его локальными правами на запуск и активацию.После проведения данных настроек вы сможете вызывать создание объекта Новый COMОбъект("Word.Application") непосредственно в процедурах с директивой выполнения &НаСервере, однако помните, что свойство Visible всегда должно быть установлено в значение Ложь, так как интерактивного экрана на сервере нет.
Рассмотрим современный профессиональный подход, который набирает все большую популярность у разработчиков высоконагруженных систем. Формат файлов .docx представляет собой обычный ZIP-архив, внутри которого находятся структурированные XML-файлы с текстом документа и элементами оформления.
Мы можем изменять данные внутри документа напрямую, не используя медленные и ресурсоемкие COM-соединения Windows. Этот метод работает молниеносно и, что критически важно, функционирует на серверах под управлением ОС Linux.
Алгоритм работы метода:
.docx в макет как двоичные данные.ЧтениеZipФайла распаковываем структуру шаблона во временный рабочий каталог на сервере.word/document.xml, где хранится весь текст нашего документа.СтрЗаменить) подставляем нужные значения параметров вместо маркеров-заполнителей..docx с помощью объекта ЗаписьZipФайла.Рассмотрим пример программной реализации этого метода на сервере:
&НаСервере
Функция СформироватьДокументБезCOM()
// Получаем двоичные данные исходного шаблона docx
МакетДвоичныеДанные = РеквизитФормыВЗначение("Объект").ПолучитьМакет("ДоговорШаблон");
// Создаем пути для временного хранения распакованных файлов
ИмяВременногоАрхива = ПолучитьИмяВременногоФайла(".zip");
МакетДвоичныеДанные.Записать(ИмяВременногоАрхива);
КаталогРаспаковки = ПолучитьИмяВременногоФайла("");
// Распаковываем документ docx как zip-архив
Архив = Новый ЧтениеZipФайла(ИмяВременногоАрхива);
Архив.ИзвлечьВсе(КаталогРаспаковки, РежимВосстановленияПутейФайловZIP.Восстанавливать);
Архив.Закрыть();
// Путь к файлу с основным содержимым документа
ПутьКXML = КаталогРаспаковки + "/word/document.xml";
// Читаем содержимое XML в текстовую строку
ТекстовыйДокумент = Новый ТекстовыйДокумент;
ТекстовыйДокумент.Прочитать(ПутьКXML, КодировкаТекста.UTF8);
ТекстДокумента = ТекстовыйДокумент.ПолучитьТекст();
// Выполняем быструю замену параметров-заполнителей
ТекстДокумента = СтрЗаменить(ТекстДокумента, "{Контрагент}", "ООО ""Вектор""");
ТекстДокумента = СтрЗаменить(ТекстДокумента, "{Сумма}", "150 000 рублей");
// Перезаписываем измененный XML
ТекстовыйДокумент.УстановитьТекст(ТекстДокумента);
ТекстовыйДокумент.Записать(ПутьКXML, КодировкаТекста.UTF8);
// Собираем измененные файлы обратно в документ docx
ИмяГотовогоДокумента = ПолучитьИмяВременногоФайла(".docx");
ЗаписьАрхива = Новый ЗаписьZipФайла(ИмяГотовогоДокумента);
ЗаписьАрхива.Добавить(КаталогРаспаковки + "/*", РежимСохраненияПутейZIP.СохранятьОтносительныеПути, РежимСжатияZIP.Максимальный);
ЗаписьАрхива.Записать();
// Помещаем готовый заполненный файл во временное хранилище для передачи на клиент
ДвоичныеДанныеРезультат = Новый ДвоичныеДанные(ИмяГотовогоДокумента);
Возврат ПоместитьВоВременноеХранилище(ДвоичныеДанныеРезультат, УникальныйИдентификатор);
КонецФункции
Этот способ работает в десятки раз быстрее, чем инициализация тяжелого COM-сервера Microsoft Word, и гарантирует абсолютную стабильность серверных процессов 1С при любых объемах формирования документации.