Как программно закрыть окно внешнего приложения, запущенного из 1С?

Программист IT и автоматизация бизнеса
← На главную

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

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

Мы проанализируем четыре практических способа решения этой задачи для операционной системы Windows, а также затронем кроссплатформенные альтернативы. Каждый метод имеет свои особенности, преимущества и ограничения, которые мы детально рассмотрим ниже.


Способ 1. Адресное закрытие конкретного процесса по его уникальному ID (PID)

Представим ситуацию: на компьютере пользователя может быть открыто несколько документов Блокнота. Если мы решим закрыть Блокнот просто по имени исполняемого файла notepad.exe, операционная система завершит абсолютно все процессы Блокнота. При этом пользователь потеряет свои личные несохраненные файлы, что вызовет обоснованный гнев. Чтобы избежать подобных инцидентов, нам нужно закрывать строго то окно, которое открыла наша сессия 1С.

Для реализации точечного закрытия мы воспользуемся COM-объектом WScript.Shell и его методом Exec. Этот метод, в отличие от стандартного запуска, возвращает специальный объект управления процессом, из которого мы можем извлечь уникальный идентификатор процесса — ProcessID (PID). Зная PID, мы сможем закрыть именно наше окно.

Разберем по шагам пример реализации этого подхода:


// Шаг 1: Инициализируем системную оболочку Windows
Шелл = Новый COMОбъект("WScript.Shell");

// Формируем путь к файлу и запускаем его через Exec.
// Обратите внимание: мы явно указываем исполняемое приложение.
ИмяФайла = "C:\temp\report.txt";
СтрокаЗапуска = "notepad.exe " + ИмяФайла;

// Метод Exec запускает программу и возвращает объект WshScriptExec
Попытка
    ЗапущенныйПроцесс = Шелл.Exec(СтрокаЗапуска);
    ИдентификаторПроцесса = ЗапущенныйПроцесс.ProcessID;
Исключение
    Сообщить("Не удалось запустить приложение: " + ОписаниеОшибки());
    Возврат;
КонецПопытки;

// ... Здесь выполняется какой-то ваш программный код в 1С ...
// Например, мы можем ожидать определенного действия или завершения фонового задания.

// Шаг 2: Закрываем именно запущенный нами процесс по его PID
// Используем утилиту taskkill с флагом /F (принудительное завершение) и /PID
КомандаЗакрытия = "taskkill /F /PID " + ИдентификаторПроцесса;

// Параметр "0" скрывает черное окно консоли CMD, "Истина" заставляет 1С дождаться завершения команды
Шелл.Run(КомандаЗакрытия, 0, Истина);

В данном примере мы использовали параметр 0 в методе Run, чтобы системная консоль не мелькала на экране и не смущала пользователя. Флаг /F в утилите taskkill означает "Force" — принудительное уничтожение процесса. Это гарантирует, что окно закроется мгновенно.


Способ 2. Быстрое скрытое закрытие процесса по имени программы

Бывают ситуации, когда нам заранее известно, что запущенное приложение работает в монопольном или фоновом режиме, либо у пользователя гарантированно не будет открыто других окон этого приложения. В таких случаях можно использовать более простой и быстрый способ — закрытие по имени исполняемого файла (например, notepad.exe, excel.exe или acrord32.exe).

Для этого мы также используем системную утилиту Windows taskkill, но уже с флагом /IM (Image Name), который указывает имя образа процесса.

Проанализируем пример кода:


// Инициализируем объект для работы с операционной системой
Шелл = Новый COMОбъект("WScript.Shell");

// Формируем команду для закрытия всех процессов Блокнота
ИмяПроцесса = "notepad.exe";
КомандаЗакрытия = "taskkill /F /IM " + ИмяПроцесса;

// Запускаем команду скрытно (параметр 0) без ожидания завершения (Параметр Ложь)
Шелл.Run(КомандаЗакрытия, 0, Ложь);

Важное примечание для кроссплатформенных решений: Если ваши пользователи работают на клиентах под управлением операционных систем семейства Linux (например, Ubuntu или Astra Linux), объект WScript.Shell там недоступен. Однако саму идею закрытия по имени приложения можно реализовать через стандартную команду 1С КомандаСистемы(), используя встроенные утилиты Linux:


// Пример для операционной системы Linux
// Завершаем процесс gedit (стандартный текстовый редактор)
КомандаСистемы("killall -9 gedit");

Способ 3. Мягкое закрытие окна через имитацию нажатия клавиш

Принудительное уничтожение процесса с помощью команды taskkill /F имеет один существенный недостаток: если в файле были сделаны изменения, они будут безвозвратно утеряны. Программа просто закроется без сохранения данных. Если перед нами стоит задача закрыть окно "мягко" — так, как если бы пользователь сам кликнул по крестику в углу экрана или нажал комбинацию клавиш Alt + F4 — нам потребуется другой подход.

Мы можем активировать нужное окно по его заголовку, а затем отправить ему виртуальное нажатие клавиш. Для этого у объекта WScript.Shell существуют методы AppActivate и SendKeys.

Рассмотрим, как реализовать этот сценарий:


Шелл = Новый COMОбъект("WScript.Shell");

// Заголовок окна, которое мы ищем. 
// Метод AppActivate может принимать как точный заголовок, так и его начальную часть.
ЗаголовокОкна = "report.txt — Блокнот";

// Пытаемся сделать окно активным (перевести на него фокус Windows)
Если Шелл.AppActivate(ЗаголовокОкна) Тогда
    
    // Если окно успешно активировано, отправляем ему комбинацию Alt + F4.
    // В синтаксисе SendKeys знак процента "%" означает клавишу ALT.
    // Заключенная в фигурные скобки "F4" указывает на соответствующую функциональную клавишу.
    Шелл.SendKeys("%{F4}");
    
КонецЕсли;

Этот метод чрезвычайно удобен, когда открытая программа при наличии изменений должна сама спросить пользователя: "Сохранить изменения перед выходом?". Однако учитывайте особенности этого механизма:

  1. Метод SendKeys очень чувствителен к текущему фокусу ввода. Если в момент отправки клавиш пользователь активно кликает мышкой в другом приложении, фокус может сместиться, и комбинация клавиш уйдет "не туда".
  2. Метод зависит от языковой раскладки клавиатуры и системного языка интерфейса.
  3. Этот способ подходит исключительно для интерактивной работы на стороне клиента под управлением Windows.

Способ 4. Использование технологии WMI (Windows Management Instrumentation)

Для наиболее требовательных разработчиков, которые хотят полностью контролировать окружение операционной системы без вызова консольных утилит типа taskkill, отлично подойдет технология WMI. WMI — это мощный встроенный интерфейс управления Windows, доступный из 1С через COM-соединение.

С помощью WMI мы можем запустить детальный поиск среди всех запущенных в системе процессов. Мы можем проверить не только имя исполняемого файла, но и командную строку, с которой он был запущен, чтобы точечно идентифицировать наш файл.

Давайте разберем этот продвинутый способ на примере:


// Подключаемся к локальной службе WMI
Локатор = Новый COMОбъект("WbemScripting.SWbemLocator");
СлужбыWMI = Локатор.ConnectServer();

// Выполняем SQL-подобный запрос для получения всех процессов Блокнота
ЗапросWMI = "SELECT * FROM Win32_Process WHERE Name = 'notepad.exe'";
СписокПроцессов = СлужбыWMI.ExecQuery(ЗапросWMI);

ИскомыйФайл = "report.txt";

// Перебираем коллекцию запущенных процессов
Для Каждого Процесс Из СписокПроцессов Цикл
    
    // Свойство CommandLine содержит полную строку, с которой был запущен процесс.
    // Мы проверяем, упоминается ли в ней имя нашего файла.
    Если Найти(Процесс.CommandLine, ИскомыйФайл) > 0 Тогда
        
        // Завершаем процесс на системном уровне
        Процесс.Terminate();
        Сообщить("Процесс для файла " + ИскомыйФайл + " успешно закрыт.");
        
    КонецЕсли;
    
КонецЦикла;

Использование WMI — это наиболее надежный программный способ на уровне операционной системы Windows. Он не зависит от фокуса ввода (как SendKeys) и позволяет очень гибко фильтровать запущенные программы по множеству параметров (время запуска, путь к исполняемому файлу, имя владельца процесса и т.д.).


Альтернативный подход: вывод содержимого файла на форму 1С

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

Гораздо элегантнее прочитать содержимое текстового файла прямо средствами встроенного языка 1С и вывести его в красивом и удобном виде на форму нашей обработки или документа. В этом случае проблема закрытия окна отпадет сама собой — форма закроется стандартными методами платформы при закрытии владельца.

Выясним, как это реализовать стандартными объектами 1С:


// Создаем объект для чтения текстовых файлов
ЧтениеТекста = Новый ЧтениеТекста;
ПутьКФайлу = "C:\temp\report.txt";

Попытка
    // Открываем файл с явным указанием кодировки (например, UTF-8)
    ЧтениеТекста.Открыть(ПутьКФайлу, КодировкаТекста.UTF8);
    СодержимоеФайла = ЧтениеТекста.Прочитать();
    ЧтениеТекста.Закрыть();
Исключение
    Сообщить("Ошибка чтения файла: " + ОписаниеОшибки());
    Возврат;
КонецПопытки;

// Теперь мы можем поместить переменную СодержимоеФайла в реквизит формы 
// типа "Строка" и отобразить его на форме в виде многострочного поля ввода.

Преимущества такого подхода очевидны:

Мы подробно рассмотрели все возможные варианты управления жизненным циклом внешних приложений из 1С. Выбирайте наиболее подходящий способ под ваши технические требования и бизнес-задачи!

← На главную