уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

В прошлой статье, посвященной консольным приложениям, я рассматривал случаи, когда работа с консольными приложениями не только необходима, но и неизбежна. Это случай работы с математическими моделями, которые разрабатываются математиками на Фортране. Нет, конечно, для любой задачи может быть, как минимум, два решения. Например, можно открыть исходник реализации модели и заново его переписать на Delphi. Но зачем? Можно просто научиться взаимодействовать с готовых консольным приложением и решить задачу намного проще. И именно этим мы сегодня и займемся — работой с консольными приложениями в Delphi, а именно — научимся работать с потоком ввода/вывода.

Когда под «взаимодействием» с консольным приложением подразумевается обычный его (консольного приложения) запуск, то, на мой взгляд, такой вопрос можно решить одним простеньким запросом в Гугл и посмотреть, например, как работать с ShellExecute или CreateProcess.

Совсем другое дело, когда нам необходимо не только запустить exe-файл, но и, по мере необходимость, передавать консольному приложению какие-либо значения, считывать вывод и определять, когда приложение завершает свою работу и так далее. При этом не плохо было бы работать с консольным приложением в отдельном потоке. Здесь уже не обойтись без серьезного изучения вопроса по работе с потоком ввода/вывода.

Тема, в принципе, столь же стара, как и Delphi, а то и ещё старее, поэтому я не буду долго копаться в теории, расписывать процесс взаимодействия по шагам и так далее (всё это прекрасно изучено и рассказано), а перейду сразу к рассмотрению готового решения для Delphi.

Russell Libby

Russel Libby — это набор из трех компонентов: TPipeClient, TPipeServer и TPipeConsole. С некоторыми доработками эти компоненты можно сказать здесь или моей страницы «Исходники». Компоненты появились в, теперь уже, далеком 2003 году, однако на сегодняшний день сайт разработчика не доступен, а разработку в своё время продолжал Francois Piette. Компоненты очень хорошие, просты в работе и имеют минимум свойств, поэтому, разобраться с ними достаточно просто и легко.

После установки в палитре компонентов Delphi появится новая вкладка с тремя перечисленными выше компонентами:

Сегодня я буду рассматривать компонент TPipeConsole.

Консольное приложение на Delphi

Прежде, чем приступать к взаимодействию со сложными консольными приложениями, напишем простенькое самостоятельно. Я не стал долго мудрить и написал консольное приложение, которое выводит таблицу умножения для любого числа в диапазоне от 0 до 9:

program test;
 
{$APPTYPE CONSOLE}
 
{$R *.res}
 
 
uses
  System.SysUtils, System.StrUtils;
 
var
  Number: integer;
  i:integer;
 
begin
  try
    WriteLn('Введите любое целое число от 1 до 9: ');
    Flush(Output);
    Read(Number);
    if (Number<1) or (Number>9) then
      raise Exception.Create('Неверное число');
    WriteLn('Таблица умножения для ' + IntToStr(Number));
    for I := 1 to 9 do
      Writeln(Format('%d х %d = %d',[Number, I, Number*1]));
    Readln;
  except
    on e: Exception do
    begin
      WriteLn(ErrOutput, e.Message);
      ExitCode := 1;
    end;
  end;
end.

Если пользователь вводит неверное число, например, 0 или 10, приложение сообщит об этом и завершит работу с кодом ошибки 1. Наша задача состоит в том, чтобы из VCL-приложения запустить консольное, ввести число и прочитать результат работы консольного приложения, включая и возможную ошибку ввода.

VCL Application

Тестовое VCL-приложение выглядит следующим образом:

На форме расположены:

  1. TEdit и TUpDown для задания числа
  2. TMemo для вывода результатов работы консольного приложения
  3. TButton для запуска консольного приложения
  4. TPipeConsole для взаимодействия с консольным приложением.

Использование TPipeConsole

Договоримся, что exe-файл консольного приложения будет располагаться в той же директории, что и exe-файл vcl-приложения.

Первое, что мы сделаем — это запустим консольное приложение и прочитаем поток ввода/вывода. Запустить консольное приложение можно следующим образом:

procedure TForm8.btnRunClick(Sender: TObject);
begin
  PipeConsole1.Start('test.exe',EmptyStr);
end;

Метод Start имеет следующее описание:

function Start(Application, CommandLine : string) : Boolean;

Application — путь к exe-файлу консольного приложения (в нашем случае это строка «test.exe»), а в CommandLine можно использовать, если консольному приложению при запуске необходимо передавать какие-либо параметры.
После запуска vcl-приложения и нажатия на кнопку «Запуск» ничего не произойдет. Точнее даже так: Вы не увидите, что происходит. Для того, чтобы окно консольного приложения стало видимым, необходимо изменить свойство Visible компонента TPipeConsole, сделав его равным True:

Теперь, если снова запустить приложение и нажать кнопку «Запуск», можно увидеть примерно следующее:

Консольное приложение запускается. Теперь необходимо прочитать поток ввода/вывода. Для этого, у компонента TPipeConsole определено событие:

property OnOutput : TOnConsole read FOnOutput write FOnOutput;
type
  TOnConsole = procedure(Sender : TObject; Stream : TStream) of object;

Воспользуемся этим событием. Пишем следующий обработчик события OnOutput:

procedure TForm8.PipeConsole1Output(Sender: TObject; Stream: TStream);
begin
  Memo1.Lines.LoadFromStream(Stream);
end;

Снова запускаем vcl-приложение и смотрим на результат:

Как видите, поток ввода/вывода был прочитан вплоть до момента, когда консольное приложение ожидает ввода числа.

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

procedure TForm8.PipeConsole1Output(Sender: TObject; Stream: TStream); 
begin 
  Memo1.Lines.LoadFromStream(Stream); 
end;

Событие OnStop вызывается при остановке консольного приложение и имеет следующее описание:

property OnStop    : TOnConsoleStop read FOnStop write FOnStop;
type
  TOnConsoleStop = procedure(Sender : TObject; ExitValue : LongWord) of object;

ExitValue— код с которым завершается приложение.
Для события OnStop напишем такой обработчик:

procedure TForm8.PipeConsole1Stop(Sender: TObject; ExitValue: Cardinal);
begin
  Memo1.Lines.Add('Приложение завершило работу с кодом: '+ExitValue.ToString);
end;

Теперь, определив все необходимые события, научимся передавать консольному приложению какие-либо значения. Для этого, у TPipeConsole имеется специальный метод:

procedure Write(const Buffer; Length : Integer);

Чтобы передать число из TEdit vcl-приложения в поток ввода/вывода консольного приложения допишем обработчик события OnOutput следующим образом:

procedure TForm8.PipeConsole1Output(Sender: TObject; Stream: TStream);
var
   bytes: TBytes;
begin
  Memo1.Lines.LoadFromStream(Stream);
  if Pos('Таблица', Memo1.Lines.Text)=0 then
    begin
      bytes := TEncoding.GetEncoding('Windows-1251').GetBytes(edNumber.Text + #13#10);
      PipeConsole1.Write(bytes[0], Length(bytes));
    end;
end;

Теперь, если в Memo будет содержаться только запрос на ввод числа, то оно передастся в консольное приложение и далее приложение продолжит свою работу или же, если число будет не верным, то в Memo появится текст и код ошибки. Проверим так ли это. Во-первых, передадим в приложение значение «0» — это должно привести к выводу сообщения об ошибке:

Как видите, всё в порядке — передача консольному приложению числа ноль привело к ошибке. Теперь передадим любое другое верное значение. Снова запускаем vcl-приложение, устанавливаем в

число и жмем кнопку «Запуск». результат:

Как видите, консольное приложение корректно получило значение «9» и вывела необходимые значения, а TPipeConsole эти значения перехватил и передал нам.

В целом, компонент TPipeConsole оказался достаточно удобным и простым в работе. И, в настоящее время, именно этот компонент я использую при разработке GUI для большой модели Aermod, работа которой разнесена сразу по 5 различным консольным приложениям.

Рассмотренный в статье пример работы с компонентом TPipeConsole можно скачать со страницы «Исходники«
0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
2 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
uranic
uranic
07/05/2019 11:47

Спасибо посмотрим. К слову я использовал из JEDI компонет TJvCreateProcess с похожим функционалом (правда с консоли не вводил)