При интеграции 1С с внешним программным обеспечением часто возникает задача запуска консольного приложения не просто в режиме "запустил и забыл", а в режиме интерактивного диалога. Это актуально для работы с SIP-клиентами (например, LinPhone), утилитами криптографии (openssl, cryptcp) или специализированными парсерами. Основная сложность заключается в том, что штатные средства 1С, такие как ЗапуститьПриложение, не предоставляют прямого доступа к потокам STDIN (ввод) и STDOUT (вывод) работающего процесса в реальном времени.
В этой статье мы подробно рассмотрим несколько подходов к решению этой задачи: от использования стандартных COM-объектов Windows до применения внешних компонент и .NET-стека.
Проанализируем наиболее доступный способ, не требующий установки сторонних библиотек. В составе ОС Windows имеется COM-объект WScript.Shell, у которого, помимо привычного метода Run, есть метод Exec. В отличие от Run, метод Exec возвращает объект WshScriptExec, который открывает доступ к потокам управления процессом.
Разберем реализацию этого метода по шагам. Основная идея заключается в том, чтобы запустить процесс и в цикле опрашивать его поток вывода, одновременно имея возможность отправить команду в поток ввода.
Рассмотрим пример кода:
// Создаем объект оболочки
WshShell = Новый COMОбъект("WScript.Shell");
// Запускаем приложение (например, консольный SIP-клиент)
// Важно: метод Exec возвращает объект, через который мы будем общаться
Процесс = WshShell.Exec("C:\PathToApp\console_app.exe");
// Чтение вывода (STDOUT)
Если Не Процесс.StdOut.AtEndOfStream Тогда
ТекстВывода = Процесс.StdOut.ReadAll();
Сообщить(ТекстВывода);
КонецЕсли;
// Отправка команды (STDIN)
Процесс.StdIn.WriteLine("help");
Важный нюанс: Метод ReadAll() является блокирующим — он будет ждать завершения работы приложения. Для интерактивной работы нам нужно читать данные порциями. Рассмотрим, как реализовать мониторинг вывода без блокировки интерфейса 1С.
Проанализируем ситуацию: чтобы 1С "слышала" приложение, нам нужно использовать ОбработчикОжидания. В нем мы будем проверять наличие данных в потоке StdOut:
// В процедуре запуска
Процесс = WshShell.Exec("my_app.exe");
ПодключитьОбработчикОжидания("ПроверитьВыводКонсоли", 0.5); // каждые 0.5 сек
// В процедуре обработчика
Процедура ПроверитьВыводКонсоли() Экспорт
Если Процесс.Status = 0 Тогда // 0 - процесс еще выполняется
Пока Не Процесс.StdOut.AtEndOfStream И Процесс.StdOut.Readable() Цикл
// Читаем по одной строке, чтобы не "зависнуть"
СтрокаВывода = Процесс.StdOut.ReadLine();
Если ЗначениеЗаполнено(СтрокаВывода) Тогда
ОбработатьОтветПриложения(СтрокаВывода);
КонецЕсли;
КонецЦикла;
Иначе
ОтключитьОбработчикОжидания("ПроверитьВыводКонсоли");
Сообщить("Приложение завершило работу");
КонецЕсли;
КонецПроцедуры
Если ваша задача требует мгновенной реакции на события (например, когда SIP-клиент сообщает о входящем звонке), опрос через обработчик ожидания может быть недостаточно быстрым или создавать лишнюю нагрузку. В этом случае верным решением будет использование Внешней Компоненты (ВК).
Выясним преимущества этого подхода. Компонента, написанная на C++ или C#, может создать отдельный поток в операционной системе, который будет непрерывно слушать STDOUT консольного приложения. Как только в потоке появляется новая строка, компонента генерирует ВнешнееСобытие, которое 1С перехватывает в процедуре ПриВнешнемСобытии.
Основные этапы работы с ВК:
УстановитьВнешнююКомпоненту.Это единственный способ добиться "событийной" модели, о которой спрашивал автор темы. Готовые компоненты для управления процессами можно найти на специализированных ресурсах для разработчиков 1С.
Для систем на базе Windows отличным вариантом является использование возможностей .NET Framework. Класс System.Diagnostics.Process предоставляет гораздо более гибкое управление потоками, чем старый WScript.Shell.
Разберем, как это работает. Через специализированные библиотеки-обертки (например, использование подсистемы интеграции с .NET) мы можем напрямую управлять объектом процесса. Рассмотрим логику настройки:
StartInfo.RedirectStandardOutput = true.StartInfo.RedirectStandardInput = true.StartInfo.UseShellExecute = false (это обязательно для перенаправления потоков).Проанализируем пример настройки объекта в стиле .NET:
// Псевдокод настройки процесса через .NET Bridge
Process = Новый NetObject("System.Diagnostics.Process");
Process.StartInfo.FileName = "linphone-daemon.exe";
Process.StartInfo.Arguments = "--pipe";
Process.StartInfo.RedirectStandardOutput = Истина;
Process.StartInfo.RedirectStandardInput = Истина;
Process.StartInfo.UseShellExecute = Ложь;
Process.Start();
// Запись команды
Process.StandardInput.WriteLine("call 101");
Иногда управление через STDIN/STDOUT является "костыльным" методом, если само приложение поддерживает более продвинутые способы связи. Рассмотрим ситуацию на примере LinPhone. У этого приложения есть режим демона, который может принимать команды через локальные сокеты или именованные каналы (Named Pipes).
Если приложение умеет слушать TCP-порт, мы можем использовать объект 1С HTTPСоединение или низкоуровневые компоненты для работы с сокетами. Это гораздо стабильнее, так как избавляет от проблем с кодировками консоли (например, когда 1С ожидает UTF-8, а консоль выдает CP866) и буферизацией вывода Windows.
Разберем важный технический момент, с которым сталкиваются многие разработчики. Даже если вы правильно реализовали чтение потока, данные могут приходить "пачками" или только после закрытия приложения. Это происходит из-за буферизации на стороне операционной системы или самого приложения.
Для борьбы с этим можно попробовать:
stdbuf -oL, которая принудительно включает построчную буферизацию.StdIn всегда добавляйте символ переноса строки и, если возможно, вызывайте метод Flush().Подводя итог, можно сказать: полноценно "скармливать" ввод и получать вывод в 1С можно. Для простых задач идеально подойдет WScript.Shell с методом Exec и опросом через обработчик ожидания. Для сложных профессиональных решений, требующих событийной модели и высокой скорости реакции, следует смотреть в сторону написания или поиска готовой внешней компоненты на Native API.