Запуск консольных приложений и перехват потока ввода/вывода в Delphi XE3
Предположим вам нужно запустить консольное приложение из оконного приложения и наблюдать за потоком вывода. Вы можете сохранять выводимый текст в файл журнала или отображать текст в окне в многострочном текстовом поле, например, в TRichEdit. Кроме того вы можете отправить информацию приложению, если оно что-то запросило. Давайте разберёмся, как это сделать?
Запуск консольных приложений или любых других приложений в Delphi делается очень просто, с помощью функции CreateProcess. Сложнее обстоит дело, если вам нужно запустить консольное приложение и при этом произвести перехват потока ввода/вывода. Для этого вам нужно создать три канала, два для перехвата потоков вывода (один для ошибок, второй для всего остального) и один для входного потока, а затем запустить процесс, передав ему эти каналы. В этом случае вы сможете считывать информацию, которую отдаёт консольное приложение и управлять им.
Как написать этот код с нуля можно найти в Интернете (например, на этом форуме), здесь же я остановлюсь на компоненте TPipeConsole, который как раз и создан, чтобы перехватывать потоки ввода/вывода в Delphi. Причём он позволяет это делать комфортно.
Компонент входит в состав юнита Pipes.pas, про который я писал в статье «Обмен данными между процессами в Delphi XE3». Там же написано, как лучше установить компоненты. Мой исправленный вариант можно скачать здесь:
Pipes.pas Версия:от 12.01.2010 | |
(Старая версия!!! Лучше использовать версию Pipes.pas (Win32 и Win64), см. ниже). В юните реализация классов TPipeServer (Pipe-сервер), TPipeClient (Pipe-клиент) и TPipeConsole (класс для запуска консольных приложений, управления ими и перехвата потока вывода). Работает только на платформе Win32. Юнит с моими правками для работы с Delphi до версии XE3. Функция TPipeConsole.Execute с моими правками. Источник здесь. |
|
31.01.2014 Windows 135.7 KB 3264 |
Есть вариант юнита c поддержкой всех версий Delphi и платформы Win64:
Pipes.pas (Win32 и Win64) Версия:от 04.10.2013 | |
Юнит Pipes.pas с примерами, с runttime и designtime библиотеками и поддержкой платформы Win64. В юните Pipes.pas реализация классов TPipeServer (Pipe-сервер), TPipeClient (Pipe-клиент) и TPipeConsole (класс для запуска консольных приложений, управления ими и перехвата потока вывода). Должна работать с Delphi всех версий, но тестирование не проводилось. Функция TPipeConsole.Execute с моими правками. Источник здесь. 27.04.2017 Добавлены мои правки в функции TPipeConsole.Execute и TPipeConsole.Start, чтобы параметр CommandLine 100%-но не был константой, чтобы избежать AV, см. MSDN. |
|
08.05.2015 147.99 KB 3683 |
Запуск консольного приложения и перехват потока ввода/вывода
Собственно компонент TPipeConsole всё что нам нужно уже делает. Для того, чтобы опробовать как работает запуск консольных приложений с помощью этого компонента создадим консольное приложение и скомпилируем его:
Теперь сделаем проект оконного приложения, на форму положим компонент TRichEdit, растянем его на всю форму (Align -> alClient), включим вертикальную прокрутку (ScrollBars -> ssVertical). Теперь положим на форму компонент TPipeConsole и обработаем все его события:
Теперь по событию формы OnCreate сделаем запуск нашего консольного приложения:
Выполним наше оконное приложение. Вот результат:
Как мы видим, всё отлично отработало, но есть одна ложка дёгтя при работе с компонентом. Если перемешать обычные сообщения с сообщениями об ошибке, то получать мы их будем в произвольном порядке. Вот пример такого консольного приложения:
Здесь сообщение об ошибке записывается между сообщениями 1 и 2. А вот как будет выглядеть результат:
Как видите, сначала вывелись все обычные сообщения, а потом сообщение об ошибке. Почему так происходит? Так происходит потому, что компонент TPipeConsole принимает сообщения из двух каналов по очереди: сначала из основного выходного канала, затем из канала для ошибок. Это нужно учитывать при написании консольного приложения, например, после вывода ошибки работа приложения сразу завершается. Или выводить все сообщения в основной канал.
Также здесь я отмечу, что мы использовали асинхронное выполнение консольного приложения. Если вам нужно выполнить консольное приложение синхронно, то нужно воспользоваться функцией Execute.
Теперь попробуем отправить что-нибудь консольному приложению. Для этого слегка перепишем консольное приложение:
Как видите, консольное приложение ждёт число в интервале от 0 до 9, чтобы вычислить квадрат этого числа. Значит нужно передать консольному приложению число:
Вот результат:
Попробуем теперь передать строку вместо числа:
И, после запуска, увидим ошибку:
Остановка консольного приложения
Теперь пара слов о том, как остановить консольное приложение, которое долго выполняется. Здесь есть 2 способа: прервать выполнение или оповестить приложение, что ему нужно завершить работу. Второй способ предпочтительнее, т.к. в этом случае консольное приложение перед завершением работы может освободить занимаемые ресурсы, удалить временные файлы и т.п.
Прерывание выполнения делается с помощью функции Stop:
Оповещение приложения можно сделать с помощью функций SendCtrlC и SendCtrlBreak. Вот пример вызова:
По умолчанию, вызов этой функции тоже прерывает работу приложения, но этот вызов можно обработать внутри консольного приложения по-своему и прерывания работы не произойдёт. Вот пример консольной программы с обработкой сигналов:
Теперь положим на форму нашего оконного приложения кнопку и по событию OnClick вызовем функцию SendCtrlC:
Теперь протестируем, как работает отправка сигнала консольному приложению. Запустим нашу программу для тестирования, посмотрим, как приходят сообщения от консольного приложения, и нажмём на кнопку, которую мы сделали. Приложение завершило работу? выдав сообщение «Прерывание! Выход из цикла»:
Как видите, ничего сложного нет. Единственный момент, который нужно учесть, вызов функции Ctrl_Handler в консольном приложении происходит в отдельном потоке, поэтому при работе с глобальными переменными нужно использовать критические секции, чего в примере не сделано, чтобы не замусоривать код примера.
Итак, как вы убедились, с помощью компонента TPipeConsole можно легко контролировать консольное приложение, не углубляясь в тонкости этого процесса.
Tags: Обзоры инструментов для программирования Учебники по программированию Консольное приложение Delphi
Комментарии
Компонент TPipeConsole нужен для перехвата потока вывода от консольного приложения. Если вы хотите просто запустить приложение и поток вывода вам не нужен, то и этот компонент вам тоже не нужен. Достаточно просто запустить процесс с помощью функции CreateProcess. Параметры, которые вы перечислили выше, можно использовать в обоих случаях.
Пока ждал ответа - практически полностью написал программу...
Использовал CreateProcess и выводил сообщения из консоли в Мемо через поток в памяти...
За основу взял код от сюда:
sql.ru/.../...
RSS лента комментариев этой записи