В практике разработки на платформе 1С:Предприятие часто возникают ситуации, когда стандартная логика сталкивается с ограничениями клиент-серверной архитектуры. Одна из классических задач, вызывающих затруднения у начинающих (и не только) разработчиков — это попытка запустить внешнее приложение или выполнить интерактивное действие на компьютере пользователя, инициировав этот процесс из серверного кода, например, из модуля менеджера документа.
В этой статье мы подробно разберем проблему, с которой столкнулся автор темы: необходимость сконвертировать PDF-файл в изображение с помощью внешней утилиты и вывести его в табличный документ. Сложность заключается в том, что механизм печати часто находится в модуле менеджера (на сервере), а запуск утилит через ЗапуститьПриложение() требует контекста клиента (если утилита установлена у пользователя) или правильной настройки сервера.
Давайте проанализируем исходную ситуацию. Разработчик пытается использовать метод ЗапуститьПриложение() внутри модуля менеджера объекта.
Почему это не работает так, как ожидается?
ЗапуститьПриложение() на сервере, система ищет этот путь на серверной машине, а не на вашем ПК.Именно поэтому попытки вызвать клиентскую процедуру напрямую или получить форму (ПолучитьФорму()) из модуля менеджера обречены на неудачу — этих объектов в памяти сервера просто не существует.
Если ваша конфигурация построена на базе БСП (а это большинство современных типовых решений, таких как ERP, УТ, КА, БП), то "велосипеды" изобретать не нужно. Разработчики библиотеки уже предусмотрели механизм для выполнения кода на клиенте при печати.
Обычно команды печати регистрируются в процедуре ДобавитьКомандыПечати в модуле менеджера. По умолчанию они выполняются на сервере. Однако, мы можем изменить это поведение.
Давайте рассмотрим, как правильно настроить команду, чтобы она передавала управление на клиент:
// В модуле менеджера документа
Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт
// Добавляем новую команду
Команда = КомандыПечати.Добавить();
Команда.Идентификатор = "ПечатьКартинкиPDF";
Команда.Представление = "Печать PDF как картинки";
// САМОЕ ВАЖНОЕ: Меняем тип команды
// Стандартно там "ВызовСерверногоМетода", нам нужен клиент
Команда.ТипКоманды = "ВызовКлиентскогоМетода";
// Указываем путь к клиентскому общему модулю или модулю формы внешней обработки
Команда.МенеджерПечати = "НашОбщийМодульПечатиКлиент";
КонецПроцедуры
Теперь, когда пользователь нажмет кнопку "Печать", БСП не будет пытаться выполнить процедуру Печать в модуле менеджера. Вместо этого, она вызовет экспортную процедуру Печать из указанного вами клиентского общего модуля (НашОбщийМодульПечатиКлиент).
А уже внутри клиентского модуля вы имеете полное право использовать:
ЗапуститьПриложение() или лучше асинхронный НачатьЗапускПриложения().Если задача подразумевает массовую печать, фоновое выполнение или вы не хотите зависеть от того, установлен ли какой-то конвертер (например, ImageMagick) на компьютере каждого бухгалтера, правильнее будет перенести логику на сервер.
Рассмотрим этот подход:
USR1CV8), необходимо дать права на чтение и выполнение файлов в папке с утилитами.Важный нюанс: Если у вас кластер серверов, ПО должно быть установлено на всех машинах кластера, либо вы должны гарантировать выполнение кода на конкретном сервере через требования назначения функциональности.
Пример серверного вызова (синхронный вариант для сервера допустим, но лучше использовать объекты ЗапускПриложения):
// Код выполняется НаСервере
ЗапуститьПриложение("C:\ServerTools\ImageMagick\convert.exe " + ПараметрыКонвертации, , Истина);
Чтобы избежать запуска внешних EXE-файлов (что не всегда безопасно и удобно отлаживать), можно воспользоваться внешними компонентами (AddIn), созданными по технологии Native API.
Существуют компоненты (многие из них с открытым исходным кодом), которые оборачивают библиотеки работы с PDF (например, PDFium) и предоставляют методы 1С для конвертации страниц PDF в картинки.
Преимущества:
ЗапуститьПриложение.В этом случае код в модуле менеджера будет выглядеть примерно так:
Компонента = Новый("AddIn.PDFComponent.PDFWrapper");
КартинкаДвоичныеДанные = Компонента.ПолучитьСтраницуКакКартинку(ПутьКФайлуPDF, НомерСтраницы);
// Далее вставляем картинку в ТабличныйДокумент
Если вам необходимо инициировать действие на клиенте именно по событию с сервера (а не когда пользователь сам нажал кнопку), можно использовать механизм Системы взаимодействия (начиная с платформы 8.3.10).
Сценарий работы:
СистемаВзаимодействия.ОтправитьСообщение().ОбработкаВнешнегоСобытия или подключенный обработчик сообщений системы взаимодействия.ЗапуститьПриложение().Это более сложный архитектурный подход, который требует развернутого сервера взаимодействия, но он позволяет серверу "общаться" с клиентом.
В обсуждении упоминался вариант с "хаками", такими как ПодключитьОбработчикОжидания. Разберем, почему это плохая практика:
Если ваша задача — печать из документа по кнопке:
ТипКоманды = "ВызовКлиентскогоМетода" и переносите логику запуска приложения в клиентский общий модуль.Если задача — фоновая обработка без участия пользователя, единственным верным решением будет установка необходимого программного обеспечения (конвертеров) непосредственно на сервер 1С.