При разработке в среде 1С программисты часто сталкиваются с необходимостью автоматического открытия группы файлов внешними приложениями. Типичный сценарий: выгрузить из базы данных пакет изображений (например, сертификаты номенклатуры в формате JPG) или документов (в формате PDF) во временный каталог, после чего последовательно запустить их просмотр для пользователя.
Для реализации этой задачи разработчики обычно используют встроенную функцию платформы ЗапуститьПриложение, помещая ее внутрь цикла обхода путей к файлам. Однако на практике возникает неприятный эффект: операционная система открывает только один (чаще всего последний или самый первый) файл из всего списка. Самое загадочное в этой ситуации — если запустить тот же самый код в режиме отладки с точкой останова внутри цикла, файлы открываются корректно один за другим. В этой статье мы подробно проанализируем физику этого процесса в операционной системе, разберем популярные решения и рассмотрим безопасные, бесконфликтные альтернативы.
Проблема кроется не в платформе 1С, а в особенностях архитектуры Windows Shell (интерфейса запуска приложений) и логике работы современных программ просмотра графики и документов. Проанализируем ситуацию по шагам:
Выясним причину влияния отладчика. Точка останова в конфигураторе искусственно создает паузу (таймаут). За время, пока разработчик нажимает клавишу продолжения отладки, операционная система успевает полностью загрузить ассоциированное приложение и подготовить его к корректному приему следующей команды запуска.
Решением, к которому приходят разработчики на форумах, является задействование третьего параметра функции ЗапуститьПриложение. Этот параметр называется ДождатьсяЗавершения. Рассмотрим пример кода:
Для Каждого ПутьКФайлу Из МассивПутей Цикл
// Третий параметр "Истина" заставляет 1С ждать закрытия файла
ЗапуститьПриложение(ПутьКФайлу, , Истина);
КонецЦикла;
Проанализируем работу этого механизма. При установке флага ожидания в значение Истина поток выполнения кода 1С полностью блокируется. Платформа ожидает, пока вызванный процесс завершит свою работу и вернет код возврата.
Этот способ гарантирует открытие абсолютно всех файлов без пропусков, однако он обладает критическими недостатками для интерфейса и удобства пользователя:
Если мы хотим открыть все файлы параллельно без блокировки интерфейса 1С, нам необходимо программно сымитировать задержку, которую давал нам отладчик. Для этого идеально подходит штатный асинхронный механизм платформы — обработчик ожидания. Разберем по шагам, как построить неблокирующую очередь.
Во-первых, на уровне модуля нашей формы объявим переменную, в которой будем хранить список файлов, подлежащих открытию:
Перем МассивФайловДляОткрытия;
Во-вторых, напишем процедуру инициализации запуска, которая соберет файлы и запустит первый шаг очереди:
Процедура НачатьОткрытиеФайлов()
// Заполняем массив путей к файлам, которые нужно открыть
МассивФайловДляОткрытия = Новый Массив;
МассивФайловДляОткрытия.Добавить("C:\Temp\image1.jpg");
МассивФайловДляОткрытия.Добавить("C:\Temp\image2.jpg");
МассивФайловДляОткрытия.Добавить("C:\Temp\image3.jpg");
// Подключаем однократный обработчик ожидания с микропаузой (например, 0.4 секунды)
ПодключитьОбработчикОжидания("ОткрытьОчереднойФайлИзОчереди", 0.4, Истина);
КонецПроцедуры
В-третьих, реализуем саму процедуру обработчика ожидания. Обратите внимание, что она должна быть объявлена с ключевым словом Экспорт:
Процедура ОткрытьОчереднойФайлИзОчереди() Экспорт
// Проверяем, остались ли файлы в очереди
Если МассивФайловДляОткрытия.Количество() = 0 Тогда
Возврат;
КонецЕсли;
// Извлекаем первый файл из массива и удаляем его из очереди
ТекущийФайл = МассивФайловДляОткрытия[0];
МассивФайловДляОткрытия.Удалить(0);
// Запускаем файл без ожидания завершения программы просмотра
ЗапуститьПриложение(ТекущийФайл, , Ложь);
// Если в очереди еще остались файлы, перезапускаем обработчик ожидания
Если МассивФайловДляОткрытия.Количество() > 0 Тогда
ПодключитьОбработчикОжидания("ОткрытьОчереднойФайлИзОчереди", 0.4, Истина);
КонецЕсли;
КонецПроцедуры
Этот изящный алгоритм дает операционной системе те самые необходимые доли секунды на корректную инициализацию процесса-просмотрщика. Все файлы откроются в отдельных окнах или вкладках, а интерфейс вашей базы данных 1С останется абсолютно отзывчивым.
При переходе конфигураций на управляемый интерфейс и веб-клиент использование синхронных вызовов становится невозможным. При попытке запустить код с параметром ожидания в режиме управляемого приложения платформа выдаст ошибку о запрете синхронных вызовов на клиенте. В современных версиях 1С (начиная с релиза 8.3.15) для решения этой проблемы были добавлены асинхронные аналоги.
Рассмотрим, как запустить приложение асинхронно с помощью метода НачатьЗапускПриложения. Этот метод выполняет работу в фоновом режиме, не замораживая интерфейс:
&НаКлиенте
Процедура ЗапуститьФайлыАсинхронно(МассивПутей)
Для Каждого ПутьКФайлу Из МассивПутей Цикл
// Создаем описание оповещения о завершении работы внешней программы
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеЗапускаПриложения", ЭтотОбъект, ПутьКФайлу);
// Метод выполняет асинхронный запуск без ожидания завершения
НачатьЗапускПриложения(ОписаниеОповещения, ПутьКФайлу);
КонецЦикла;
КонецПроцедуры
&НаКлиенте
Процедура ПослеЗапускаПриложения(КодВозврата, ДополнительныеПараметры) Экспорт
// В параметре "ДополнительныеПараметры" к нам вернется путь к файлу
// Здесь можно обработать факт успешного или неуспешного открытия файла
Если КодВозврата <> 0 Тогда
ЗаписьЖурналаРегистрации("ЗапускПриложения", УровеньЖурналаРегистрации.Предупреждение, , ,
"Не удалось запустить файл: " + ДополнительныеПараметры);
КонецЕсли;
КонецПроцедуры
Использование этого подхода позволяет писать качественный код в стандартах современной разработки 1С, полностью готовый к работе в тонком клиенте и веб-интерфейсе.
Если ваша конфигурация разработана с использованием Библиотеки стандартных подсистем (БСП), то ручной вызов методов вроде ЗапуститьПриложение напрямую из кода считается нарушением стандартов разработки. В БСП предусмотрен готовый, кроссплатформенный и безопасный механизм для запуска ассоциированных файлов. Для быстрой навигации по доступным клиентским процедурам подсистем пригодится справочник по методам БСП с примерами кода.
Рассмотрим пример открытия файлов с использованием общего модуля БСП:
Для Каждого ПутьКФайлу Из МассивПутей Цикл
// Вызов безопасного метода БСП для открытия файла по умолчанию
ФайловаяСистемаКлиент.ОткрытьФайл(ПутьКФайлу);
КонецЦикла;
Проанализируем преимущества использования функций БСП перед прямыми системными вызовами:
Если на компьютерах пользователей установлены продвинутые сторонние программы для просмотра графики или PDF (например, IrfanView, FastStone Image Viewer, Adobe Acrobat Reader или Foxit Reader), мы можем воспользоваться возможностью передачи списка файлов через аргументы командной строки.
При таком подходе вместо запуска цикла мы формируем одну общую команду, которая запускает просмотрщик один раз и передает ему пути ко всем файлам одновременно. Программа откроет их во внутренних вкладках. Разберем пример реализации:
Процедура ОткрытьПакетКартинокЧерезПрограмму()
// Путь к исполняемому файлу программы просмотра
ПутьКПрограмме = """C:\Program Files\FastStone Image Viewer\FSViewer.exe""";
// Формируем список параметров, где каждый путь обернут в двойные кавычки
ПараметрыЗапуска = "";
ПараметрыЗапуска = ПараметрыЗапуска + " ""C:\Temp\photo1.jpg""";
ПараметрыЗапуска = ПараметрыЗапуска + " ""C:\Temp\photo2.jpg""";
ПараметрыЗапуска = ПараметрыЗапуска + " ""C:\Temp\photo3.jpg""";
// Полная строка команды для запуска
СтрокаКоманды = ПутьКПрограмме + ПараметрыЗапуска;
// Запускаем приложение один раз с набором параметров
ЗапуститьПриложение(СтрокаКоманды);
КонецПроцедуры
Использование этого способа позволяет полностью избежать проблем с конкуренцией процессов в операционной системе, минимизирует нагрузку на компьютер пользователя и обеспечивает максимально быстрое открытие всего пакета необходимых документов.
Подводя итог, отметим: если вам необходимо быстро решить проблему в старой конфигурации на обычных формах, наиболее стабильным выбором будет Способ 2 (с обработчиком ожидания). Для современных конфигураций на управляемых формах всегда следует отдавать предпочтение Способу 4 (БСП) или асинхронным вызовам платформы.