При переходе с файловой информационной базы на клиент-серверный вариант работы разработчики часто сталкиваются с ситуацией, когда привычный код взаимодействия с внешними офисными приложениями перестает работать. В коде вызывается макет типа ActiveDocument или двоичные данные, запускается приложение MS Word, но на экране пользователя ничего не появляется, а в диспетчере задач сервера «намертво» зависает фоновый процесс WINWORD.EXE. Разберем по шагам, почему возникает эта проблема и как правильно организовать открытие и заполнение документов Word на клиенте, а также настроить защиту документа с доступными для ввода полями.
Проанализируем исходный фрагмент кода, выполняемый на стороне сервера:
Процедура ТестСМакетомСервер()
ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
СсылкаМакет = ОбработкаОбъект.ПолучитьМакет("Макет");
Word = СсылкаМакет.Получить();
СсылкаМакет = Word.Application.Documents(1);
СсылкаМакет.Activate();
СсылкаМакет.Application.Visible = Истина;
КонецПроцедуры
В файловой базе данных клиентская часть и серверная логика выполняются в рамках одного пользовательского процесса в интерактивной сессии Windows. Поэтому вызов СсылкаМакет.Получить() запускает приложение прямо на рабочем столе пользователя.
В клиент-серверной базе ситуация принципиально иная:
Visible = Истина в этой среде бессмысленна: операционная система не может показать графическое окно пользователю, работающему на локальном компьютере или через RDP-сессию.Чтобы документ отобразился у пользователя, запуск приложения Word и вывод интерфейса должны происходить строго на стороне клиента. Поскольку макет хранится в метаданных конфигурации, на сервере мы только считываем его и помещаем во временное хранилище, а клиентская часть сохраняет файл на диск и открывает его через локальный COM-объект.
Посмотрим на пример правильной реализации:
&НаСервере
Функция ПолучитьАдресМакетаWord()
ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
ДвоичныеДанныеМакета = ОбработкаОбъект.ПолучитьМакет("Макет");
Возврат ПоместитьВоВременноеХранилище(ДвоичныеДанныеМакета, УникальныйИдентификатор);
КонецФункции
&НаКлиенте
Процедура ОткрытьДокументWord()
АдресФайла = ПолучитьАдресМакетаWord();
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресФайла);
ИмяВременногоФайла = КаталогВременныхФайлов() + "Документ_" + СтрЗаменить(Строка(Новый УникальныйИдентификатор), "-", "") + ".docx";
ДвоичныеДанные.Записать(ИмяВременногоФайла);
Попытка
WordПриложение = Новый COMОбъект("Word.Application");
WordДокумент = WordПриложение.Documents.Open(ИмяВременногоФайла);
WordПриложение.Visible = Истина;
WordПриложение.Activate();
Исключение
Сообщить("Не удалось запустить Microsoft Word: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Разберем, что происходит в этом алгоритме:
ДвоичныеДанные и передается через функцию ПоместитьВоВременноеХранилище().ПолучитьИзВременногоХранилища() и сохраняется на локальный диск пользователя с помощью функции КаталогВременныхФайлов().Новый COMОбъект("Word.Application"), который гарантированно запускается в интерактивной пользовательской сессии.Если макет требуется только открыть для чтения или печати без программного заполнения параметров через скрипты 1С, более надежным вариантом будет отказ от COM-автоматизации в пользу прямого вызова системного приложения.
&НаКлиенте
Процедура ОткрытьДокументПоУмолчанию()
АдресФайла = ПолучитьАдресМакетаWord();
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресФайла);
ПутьКФайлу = КаталогВременныхФайлов() + "ПечатнаяФорма.docx";
ДвоичныеДанные.Записать(ПутьКФайлу);
ЗапуститьПриложение(ПутьКФайлу);
КонецПроцедуры
Платформенный метод ЗапуститьПриложение() открывает сохраненный файл в программе, сопоставленной расширению DOCX в операционной системе пользователя. Это исключает падения из-за отсутствия установленного Word (документ откроется в любом доступном редакторе) или конфликта COM-библиотек разрядности 32 и 64 бит.
Рассмотрим вторую задачу: как заблокировать документ от изменений, но оставить доступными для ввода определенные места. При вызове метода Word.ActiveDocument.Protect() весь документ по умолчанию становится статичным. Однако в объектной модели MS Word предусмотрено несколько подходов для организации частичной доступности.
wdAllowOnlyFormFields (первый параметр метода Protect со значением 2) блокируется весь текст документа, кроме элементов формы: текстовых полей, выпадающих списков и флажков. В шаблоне Word заранее создаются поля через вкладку «Разработчик», после чего документ блокируется кодом:
// Защита с разрешением редактирования только полей форм
WordДокумент.Protect(2, Истина, "Пароль123");
WordДокумент.Sections(2).ProtectedForForms = Ложь;
WordДокумент.Protect(2, Истина, "Пароль123");
3 — wdAllowOnlyReading), можно программно указать конкретный диапазон текста и назначить ему право редактирования для всех пользователей с помощью значения -1 (константа wdEditorEveryone):
// Находим нужную область или закладку
ОбластьДляВвода = WordДокумент.Bookmarks.Item("ПолеВвода").Range;
// Добавляем диапазон в исключения правки для всех пользователей
ОбластьДляВвода.Editors.Add(-1);
// Включаем режим защиты только для чтения
WordДокумент.Protect(3, Истина, "Пароль123");