Как запустить внешнее приложение из 1С и не потерять фокус?

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

При разработке в 1С часто возникает задача запустить внешнее приложение или скрипт и дождаться его завершения. Стандартная функция ЗапуститьПриложение() с параметром ожидания (третий параметр равен Истина) отлично справляется с задачей, но имеет неприятный побочный эффект: фокус ввода переключается на окно запускаемого приложения, а после его закрытия не всегда возвращается в 1С. Это может мешать пользователю и нарушать логику работы. Давайте разберем несколько способов, как можно решить эту проблему. При необходимости организовать удобный вызов внешних программ из интерфейса можно воспользоваться конфигурацией для запуска приложений и документов.

Решение 1: Предотвращение потери фокуса через скрытый запуск

Это самый элегантный и надежный способ, если пользователю не нужно видеть окно запускаемого приложения. Такой сценарий типичен для консольных утилит, конвертеров файлов или фоновых скриптов. Идея состоит в том, чтобы запустить процесс в скрытом режиме. В этом случае операционная система не создает видимого окна и, соответственно, не передает ему фокус.

Для этого воспользуемся COM-объектом WScript.Shell и его методом Run.

Посмотрим на пример:


// Строка команды, которую необходимо выполнить
// Например, запуск конвертера с параметрами
СтрокаЗапуска = "C:\Utils\converter.exe -infile C:\temp\in.txt -outfile C:\temp\out.txt";

// Создаем COM-объект для работы с оболочкой Windows
Оболочка = Новый COMОбъект("Wscript.Shell");

// Запускаем приложение методом Run
// Параметры метода Run(Команда, ВидимостьОкна, ОжидатьЗавершения)
// 0 - Запустить в скрытом окне, фокус не теряется
// 1 - Активировать и показать окно (стандартное поведение)
// Истина - Ожидать завершения процесса перед продолжением выполнения кода 1С
КодВозврата = Оболочка.Run(СтрокаЗапуска, 0, Истина);

// Освобождаем COM-объект
Оболочка = Неопределено;

// Можно проанализировать код возврата
Сообщить("Приложение завершилось с кодом: " + КодВозврата);

Разберем по шагам:

  1. Мы создаем экземпляр объекта Wscript.Shell.
  2. Вызываем его метод Run, передавая три параметра:
    • СтрокаЗапуска — полная команда для выполнения.
    • 0 — ключевой параметр. Он указывает, что окно приложения должно быть скрыто.
    • Истина — указывает, что код 1С будет ожидать, пока внешнее приложение не завершит свою работу.

В результате внешняя программа отработает в фоне, пользователь не увидит никаких "мелькающих" черных окон, а фокус останется в активном окне 1С. Это идеальное решение для фоновых задач.

Решение 2: Программный возврат фокуса после завершения приложения

Что делать, если пользователю необходимо взаимодействовать с окном внешнего приложения? В этом случае потери фокуса не избежать, но мы можем программно вернуть его обратно в 1С сразу после того, как внешняя программа будет закрыта. Рассмотрим два основных подхода.

Подход 2.1: Активация главного окна 1С по заголовку

Этот метод универсален, так как он активирует главное окно приложения 1С, независимо от того, какая форма была активна. Для этого мы снова используем WScript.Shell, но на этот раз его метод AppActivate.

Пример кода:


// Запускаем приложение, например, стандартный калькулятор, и ждем его закрытия
ЗапуститьПриложение("calc.exe",, Истина);

// После того, как калькулятор будет закрыт, выполнение кода продолжится здесь

// Получаем заголовок главного окна текущего приложения 1С
ЗаголовокОкна1С = ПолучитьЗаголовокПриложения();

// Создаем COM-объект
Попытка
    WSHShell = Новый COMОбъект("WScript.Shell");
    // Активируем окно 1С по его заголовку
    WSHShell.AppActivate(ЗаголовокОкна1С);
Исключение
    Сообщить("Не удалось вернуть фокус приложению: " + ОписаниеОшибки());
КонецПопытки;

Важный нюанс: в современных версиях Windows (10 и 11) существует механизм защиты от "кражи" фокуса (Focus Stealing Prevention). Если приложение, не являющееся активным, пытается программно переключить фокус на себя, система может этому воспрепятствовать. Вместо переключения окна иконка 1С на панели задач может просто начать мигать оранжевым. Тем не менее, во многих случаях метод AppActivate срабатывает успешно.

Подход 2.2: Активация конкретной формы

Если запуск внешнего приложения происходит из определенной формы, и именно в нее нужно вернуть фокус, можно использовать метод Активизировать() самой формы. Этот метод более специфичен и подходит для логики, завязанной на конкретный интерфейсный элемент.

Пример кода для управляемой формы:


&НаКлиенте
Процедура ЗапуститьИВернутьФокус(Команда)
    // Запускаем приложение и ждем завершения
    ЗапуститьПриложение("notepad.exe",, Истина);

    // После закрытия Блокнота возвращаем фокус на текущую форму
    ЭтаФорма.Активизировать();
КонецПроцедуры

Этот способ прост и не требует использования COM-объектов. Однако его действие может быть ограничено: он активирует форму внутри окна 1С, но если само окно 1С было свернуто или перекрыто другими окнами, этот метод может не вывести его на передний план.

Решение 3: Полный контроль над процессом через WScript.Shell.Exec

Метод Exec объекта WScript.Shell предоставляет больше контроля, чем Run. Он не блокирует выполнение кода 1С, а возвращает объект запущенного процесса. Через этот объект можно отслеживать его состояние и получить ID процесса.

Пример с ожиданием завершения:


// Строка команды
СтрокаКоманды = "ping 127.0.0.1 -n 5";

// Создаем COM-объект
мShell = Новый COMОбъект("WScript.Shell");

// Запускаем процесс через Exec
Процесс = мShell.Exec("cmd /c " + СтрокаКоманды);

// Организуем цикл ожидания завершения процесса
// Свойство Status равно 0, пока процесс выполняется
Пока Процесс.Status = 0 Цикл
    // В этом цикле можно выполнять другие действия или просто делать паузу,
    // чтобы не загружать процессор. Но для простоты оставим его пустым.
    // Для управляемых форм здесь лучше использовать асинхронную модель,
    // чтобы не "вешать" интерфейс.
КонецЦикла;

// После завершения цикла можно вернуть фокус одним из способов, описанных выше
мShell.AppActivate(ПолучитьЗаголовокПриложения());

Сообщить( случаи и современные подходы</h3>
<p>Напоследок рассмотрим несколько специфических решений.</p>
<ul>
    <li><strong>Для пакетных файлов (.bat, .cmd):</strong> Если вы запускаете .bat-файл, можно использовать внутреннюю команду <code>start</code> с ключом <code>/b</code>. Этот ключ запускает приложение в той же консоли, не создавая нового окна, что часто помогает избежать потери фокуса. Команда в 1С будет выглядеть так: <code>ЗапуститьПриложение("cmd /c start /b my_script.bat",, Истина)</code>.</li>
    <li><strong>Асинхронный запуск:</strong> В современных версиях платформы 1С (особенно в управляемых формах) синхронный запуск с ожиданием не рекомендуется, так как он блокирует интерфейс пользователя. Вместо этого следует использовать асинхронные методы: <code>НачатьЗапускПриложения()</code> с передачей <code>ОписанияОповещения</code>. Код для возврата фокуса в этом случае нужно будет разместить в процедуре-обработчике, которая вызовется по завершении внешнего приложения.</li>
</ul>
<codebig>
&НаКлиенте
Процедура КомандаВыполнитьНажатие(Команда)
    Оповещение = Новый ОписаниеОповещения("ПослеЗапускаПриложения", ЭтаФорма);
    НачатьЗапускПриложения(Оповещение, "calc.exe");
КонецПроцедуры

&НаКлиенте
Процедура ПослеЗапускаПриложения(КодВозврата, ДополнительныеПараметры) Экспорт
    Если КодВозврата = 0 Тогда
        Сообщить("Калькулятор успешно закрыт.");
    Иначе
        Сообщить("Приложение завершилось с ошибкой.");
    КонецЕсли;

    // Возвращаем фокус
    ЭтаФорма.Активизировать();
КонецПроцедуры

Выбор конкретного метода зависит от задачи: если нужно скрыть процесс — используйте WScript.Shell.Run с параметром 0. Если окно должно быть видимым — возвращайте фокус с помощью AppActivate или ЭтаФорма.Активизировать() после завершения процесса.

← На главную