Как решить проблему с открытием макета Word (Active Document) в клиент-серверном режиме 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При переходе с файловой базы данных 1С на клиент-серверный вариант работы разработчики часто сталкиваются с неприятной ситуацией: код, который годами успешно формировал печатные формы на основе макетов Microsoft Word (технология Active Document), внезапно перестает работать. На серверной стороне вызов метода Получить() для макета завершается аварийно, выдавая системную ошибку.

В этой статье мы подробно проанализируем, почему возникает данная проблема, рассмотрим внутренние ограничения платформы 1С:Предприятие и разберем три практических метода решения этой задачи — от рекомендуемого стандарта до тонкой настройки операционной системы Windows Server. Перед выполнением подобных технических работ на сервере рекомендуется провести автоматическое архивирование и тестирование всех баз 1С.

Почему возникает ошибка при вызове метода Получить() на сервере?

Давайте выясним первопричину сбоя. В исходном коде, запущенном в контексте &НаСервере, разработчики обычно пытаются выполнить следующую последовательность действий:


СсылкаМакет = ОбработкаОбъект.ПолучитьМакет("NNN");
Word = СсылкаМакет.Получить(); // Здесь происходит падение платформы

Эта конструкция содержит сразу три критические архитектурные проблемы:

  1. Ограничение доступности метода платформы. Метод Получить() у объекта типа ОболочкаActiveDocument имеет жесткое ограничение в синтакс-помощнике платформы 1С: Доступность: Толстый клиент. Попытка выполнить этот метод в тонком клиенте или на стороне сервера (где контекст исполнения принципиально иной) гарантированно вызовет исключение «Метод объекта не обнаружен».
  2. Ограничение сериализации типов данных. Объект типа ОболочкаActiveDocument не может быть передан через границу «сервер-клиент». Если вы попытаетесь вернуть полученный макет из серверной функции на клиент, платформа выдаст ошибку преобразования XDTO.
  3. Изоляция сессии службы сервера 1С (Session 0). Служба сервера 1С:Предприятия (например, под учетной записью USR1CV8) работает в изолированной системной сессии Windows, не имеющей доступа к графическому интерфейсу пользователя (так называемая Session 0). Когда код на сервере пытается инициализировать COM-объект Word.Application и сделать его видимым с помощью свойства Visible = Истина, операционная система блокирует это действие, что приводит к зависанию или аварийному завершению процесса.

Посмотрим, какими путями мы можем решить эту задачу в современных реалиях управляемого приложения. Если задачу формирования документов необходимо решить без написания программного кода, используется платформа для создания программных роботов One RPA.


Решение 1. Использование «Двоичных данных» вместо «Active Document» (Рекомендуемый стандарт)

Использование макетов типа Active Document в современных конфигурациях на управляемых формах считается устаревшим и крайне нестабильным подходом. Вместо этого мы рекомендуем перевести макет в категорию Двоичные данные. Это полностью решает проблему совместимости, избавляет от необходимости устанавливать Microsoft Office на сервер 1С и позволяет генерировать документы силами локального компьютера пользователя.

Рассмотрим пошагово алгоритм реализации этого метода:

  1. Откройте конфигуратор, найдите ваш макет и измените его тип на Двоичные данные (или создайте новый макет этого типа, загрузив туда ваш шаблонный файл .docx или .doc).
  2. Внутри шаблона Word заранее расставьте текстовые параметры-маркеры (например, {ФИОПолучателя}, {НомерДоговора}) или стандартные закладки (Bookmarks).
  3. Напишите процедуру экспорта двоичных данных во временное хранилище на сервере и последующую сборку файла на клиенте.

Проанализируем пример программного кода, реализующего этот надежный подход:

Код на стороне сервера:


&НаСервере
Функция ПолучитьМакетВоВременномХранилище()
    
    // Извлекаем двоичные данные файла 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 для сервера и гарантирует стабильную работу решения даже при работе пользователей через Веб-клиент в веб-браузерах.


Решение 2. Тонкая настройка сервера (Если формировать документ нужно строго на сервере)

Бывают ситуации, когда формирование документов Word должно происходить в фоновом режиме без участия пользователя (например, регламентными заданиями по расписанию для последующей отправки счетов клиентам по электронной почте). В этом случае перенести выполнение на клиентскую сторону невозможно, и нам придется научить серверную службу 1С корректно работать с COM-объектом Microsoft Word.

Для реализации этого сценария системному администратору потребуется выполнить тонкую настройку операционной системы Windows Server, на которой развернут сервер приложений 1С:

Шаг 1. Создание системных папок профиля Windows

Процессы Microsoft Office при инициализации через COM-интерфейс в обязательном порядке проверяют физическое наличие папки Desktop в профиле пользователя, запустившего сеанс. У системной учетной записи службы 1С (чаще всего это USR1CV8) такой папки по умолчанию нет. Вам необходимо создать её вручную:

Важно: После создания папки зайдите в её свойства безопасности и предоставьте учетной записи службы сервера 1С (USR1CV8 или LOCAL SERVICE) полные права доступа (Чтение, Запись, Изменение).

Шаг 2. Настройка прав в консоли DCOM

Для того чтобы служба сервера имела право запускать COM-объект текстового процессора, настроим разрешения распределенной объектной модели DCOM:

  1. Нажмите комбинацию клавиш Win + R на сервере, введите команду dcomcnfg и нажмите клавишу ввода. (Внимание: если сервер приложений 1С имеет разрядность x64, а установленный пакет Microsoft Office — x86, запустите консоль DCOM специальной командой: comexp.msc /32).
  2. Перейдите по дереву каталогов: Службы компонентов -> Компьютеры -> Мой компьютер -> Настройка DCOM.
  3. Найдите в списке элемент с именем Документ Microsoft Word (или Microsoft Word Application) и откройте его свойства через контекстное меню.
  4. Перейдите на вкладку Безопасность. В разделах «Разрешения на запуск и активацию» и «Разрешения на доступ» установите переключатели в положение «Настроить», нажмите кнопку «Изменить» и добавьте пользователя службы сервера 1С (USR1CV8), наделив его локальными правами на запуск и активацию.
  5. Перейдите на вкладку Удостоверение и переключите переключатель в положение «Интерактивный пользователь» или явно укажите учетные данные администратора сервера. Нажмите «Применить» для сохранения параметров.

После проведения данных настроек вы сможете вызывать создание объекта Новый COMОбъект("Word.Application") непосредственно в процедурах с директивой выполнения &НаСервере, однако помните, что свойство Visible всегда должно быть установлено в значение Ложь, так как интерактивного экрана на сервере нет.


Решение 3. Альтернативный метод без использования COM-объектов (Парсинг ZIP/XML на сервере)

Рассмотрим современный профессиональный подход, который набирает все большую популярность у разработчиков высоконагруженных систем. Формат файлов .docx представляет собой обычный ZIP-архив, внутри которого находятся структурированные XML-файлы с текстом документа и элементами оформления.

Мы можем изменять данные внутри документа напрямую, не используя медленные и ресурсоемкие COM-соединения Windows. Этот метод работает молниеносно и, что критически важно, функционирует на серверах под управлением ОС Linux.

Алгоритм работы метода:

  1. Загружаем файл-шаблон .docx в макет как двоичные данные.
  2. С помощью встроенного в платформу 1С объекта ЧтениеZipФайла распаковываем структуру шаблона во временный рабочий каталог на сервере.
  3. Считываем содержимое файла word/document.xml, где хранится весь текст нашего документа.
  4. С помощью обычной текстовой замены (функция СтрЗаменить) подставляем нужные значения параметров вместо маркеров-заполнителей.
  5. Записываем измененный 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С при любых объемах формирования документации.

← На главную