Почему не открывается макет Word в клиент-серверной базе 1С и как это исправить?

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

При переходе с файловой информационной базы на клиент-серверный вариант работы разработчики часто сталкиваются с ситуацией, когда привычный код взаимодействия с внешними офисными приложениями перестает работать. В коде вызывается макет типа ActiveDocument или двоичные данные, запускается приложение MS Word, но на экране пользователя ничего не появляется, а в диспетчере задач сервера «намертво» зависает фоновый процесс WINWORD.EXE. Разберем по шагам, почему возникает эта проблема и как правильно организовать открытие и заполнение документов Word на клиенте, а также настроить защиту документа с доступными для ввода полями.

Выясним причину: почему процесс Word зависает на сервере

Проанализируем исходный фрагмент кода, выполняемый на стороне сервера:


Процедура ТестСМакетомСервер()
    ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
    СсылкаМакет = ОбработкаОбъект.ПолучитьМакет("Макет");    
    Word = СсылкаМакет.Получить();
    
    СсылкаМакет = Word.Application.Documents(1);
    СсылкаМакет.Activate();        
    СсылкаМакет.Application.Visible = Истина;
КонецПроцедуры

В файловой базе данных клиентская часть и серверная логика выполняются в рамках одного пользовательского процесса в интерактивной сессии Windows. Поэтому вызов СсылкаМакет.Получить() запускает приложение прямо на рабочем столе пользователя.

В клиент-серверной базе ситуация принципиально иная:

  1. Изоляция сессий (Session 0 Isolation). Служба сервера 1С:Предприятия выполняется как системная служба в неинтерактивной нулевой сессии Windows. Попытка установить свойство Visible = Истина в этой среде бессмысленна: операционная система не может показать графическое окно пользователю, работающему на локальном компьютере или через RDP-сессию.
  2. Зависание на диалоговых окнах. При инициализации COM-объекта Word на сервере любое предупреждение безопасности, сообщение об активации лицензии или диалог восстановления поврежденного файла приводит к тому, что приложение ожидает нажатия кнопки в невидимом окне. Процесс зависает навсегда и потребляет ресурсы сервера.
  3. Архитектурные ограничения Microsoft. Корпорация Microsoft официально не поддерживает и не рекомендует автоматизацию пакета MS Office на стороне сервера из-за потенциальных проблем с многопоточностью, утечками памяти и лицензированием.

Решение 1: Передача макета через временное хранилище и открытие на клиенте

Чтобы документ отобразился у пользователя, запуск приложения Word и вывод интерфейса должны происходить строго на стороне клиента. Поскольку макет хранится в метаданных конфигурации, на сервере мы только считываем его и помещаем во временное хранилище, а клиентская часть сохраняет файл на диск и открывает его через локальный COM-объект.

Посмотрим на пример правильной реализации:


&НаСервере
Функция ПолучитьАдресМакетаWord()
    ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
    ДвоичныеДанныеМакета = ОбработкаОбъект.ПолучитьМакет("Макет");
    Возврат ПоместитьВоВременноеХранилище(ДвоичныеДанныеМакета, УникальныйИдентификатор);
КонецФункции

&НаКлиенте
Процедура ОткрытьДокументWord()
    АдресФайла = ПолучитьАдресМакетаWord();
    ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресФайла);
    
    ИмяВременногоФайла = КаталогВременныхФайлов() + "Документ_" + СтрЗаменить(Строка(Новый УникальныйИдентификатор), "-", "") + ".docx";
    ДвоичныеДанные.Записать(ИмяВременногоФайла);
    
    Попытка
        WordПриложение = Новый COMОбъект("Word.Application");
        WordДокумент = WordПриложение.Documents.Open(ИмяВременногоФайла);
        WordПриложение.Visible = Истина;
        WordПриложение.Activate();
    Исключение
        Сообщить("Не удалось запустить Microsoft Word: " + ОписаниеОшибки());
    КонецПопытки;
КонецПроцедуры

Разберем, что происходит в этом алгоритме:

  1. На сервере макет извлекается как ДвоичныеДанные и передается через функцию ПоместитьВоВременноеХранилище().
  2. На клиенте файл скачивается функцией ПолучитьИзВременногоХранилища() и сохраняется на локальный диск пользователя с помощью функции КаталогВременныхФайлов().
  3. Создается клиентский объект Новый COMОбъект("Word.Application"), который гарантированно запускается в интерактивной пользовательской сессии.

Решение 2: Открытие файла стандартным приложением без COM-объекта

Если макет требуется только открыть для чтения или печати без программного заполнения параметров через скрипты 1С, более надежным вариантом будет отказ от COM-автоматизации в пользу прямого вызова системного приложения.


&НаКлиенте
Процедура ОткрытьДокументПоУмолчанию()
    АдресФайла = ПолучитьАдресМакетаWord();
    ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресФайла);
    
    ПутьКФайлу = КаталогВременныхФайлов() + "ПечатнаяФорма.docx";
    ДвоичныеДанные.Записать(ПутьКФайлу);
    
    ЗапуститьПриложение(ПутьКФайлу);
КонецПроцедуры

Платформенный метод ЗапуститьПриложение() открывает сохраненный файл в программе, сопоставленной расширению DOCX в операционной системе пользователя. Это исключает падения из-за отсутствия установленного Word (документ откроется в любом доступном редакторе) или конфликта COM-библиотек разрядности 32 и 64 бит.

Настройка выборочного редактирования: защита документа с открытыми полями

Рассмотрим вторую задачу: как заблокировать документ от изменений, но оставить доступными для ввода определенные места. При вызове метода Word.ActiveDocument.Protect() весь документ по умолчанию становится статичным. Однако в объектной модели MS Word предусмотрено несколько подходов для организации частичной доступности.

  1. Использование полей формы (Form Fields). При установке режима защиты wdAllowOnlyFormFields (первый параметр метода Protect со значением 2) блокируется весь текст документа, кроме элементов формы: текстовых полей, выпадающих списков и флажков. В шаблоне Word заранее создаются поля через вкладку «Разработчик», после чего документ блокируется кодом:
    
    // Защита с разрешением редактирования только полей форм
    WordДокумент.Protect(2, Истина, "Пароль123");
    
  2. Разделение документа на секции. Если макет разбит на разделы (Sections), защиту форм можно применить выборочно. Например, запретить правку в первом разделе и разрешить во втором:
    
    WordДокумент.Sections(2).ProtectedForForms = Ложь;
    WordДокумент.Protect(2, Истина, "Пароль123");
    
  3. Исключения правки для диапазонов (коллекция Editors). Если документ переводится в режим «Только чтение» (значение константы 3 — wdAllowOnlyReading), можно программно указать конкретный диапазон текста и назначить ему право редактирования для всех пользователей с помощью значения -1 (константа wdEditorEveryone):
    
    // Находим нужную область или закладку
    ОбластьДляВвода = WordДокумент.Bookmarks.Item("ПолеВвода").Range;
    
    // Добавляем диапазон в исключения правки для всех пользователей
    ОбластьДляВвода.Editors.Add(-1);
    
    // Включаем режим защиты только для чтения
    WordДокумент.Protect(3, Истина, "Пароль123");
    
    В этом случае пользователь сможет свободно вводить текст внутри отмеченной области, а вся остальная часть шаблона будет надежно защищена от изменений.
← На главную