How to get the output from a console program in real-time?

In a Delphi XE7/8 program, I want to capture the output of a console program in REALTIME, e.g. inside a TMemo. For this I have created a test console program which outputs a text every 2 seconds:

program GetDosOutput_Console_Test;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

begin
  try
    Writeln('1 of 5');
    Sleep(2000);
    Writeln('2 of 5');
    Sleep(2000);
    Writeln('3 of 5');
    Sleep(2000);
    Writeln('4 of 5');
    Sleep(2000);
    Writeln('5 of 5');
    Sleep(2000);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Open in new window


Then I use a code I have found on http://stackoverflow.com/questions/9119999/getting-output-from-a-shell-dos-app-into-a-delphi-app

function GetDosOutput(CommandLine: string; Work: string = 'C:\'): string;
var
  SA: TSecurityAttributes;
  SI: TStartupInfo;
  PI: TProcessInformation;
  StdOutPipeRead, StdOutPipeWrite: THandle;
  WasOK: Boolean;
  Buffer: array [0 .. 255] of AnsiChar;
  BytesRead: Cardinal;
  WorkDir: string;
  Handle: Boolean;
begin
  Result := '';
  with SA do
  begin
    nLength := SizeOf(SA);
    bInheritHandle := True;
    lpSecurityDescriptor := nil;
  end;
  CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 0);
  try
    with SI do
    begin
      FillChar(SI, SizeOf(SI), 0);
      cb := SizeOf(SI);
      dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
      wShowWindow := SW_HIDE;
      hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin
      hStdOutput := StdOutPipeWrite;
      hStdError := StdOutPipeWrite;
    end;
    WorkDir := Work;
    Handle := CreateProcess(nil, PChar('cmd.exe /C ' + CommandLine), nil, nil,
      True, 0, nil, PChar(WorkDir), SI, PI);
    CloseHandle(StdOutPipeWrite);
    if Handle then
      try
        repeat
          WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
          if BytesRead > 0 then
          begin
            Buffer[BytesRead] := #0;
            Result := Result + Buffer;
            Form1.mmoOutput.Lines.Add(Result); // <<<<<<<<<<<<<<<<<<< output
          end;
        until not WasOK or (BytesRead = 0);
        WaitForSingleObject(PI.hProcess, INFINITE);
      finally
        CloseHandle(PI.hThread);
        CloseHandle(PI.hProcess);
      end;
  finally
    CloseHandle(StdOutPipeRead);
  end;
end;

Open in new window


Then I call the console program with this code:

GetDosOutput(edtConsoleExe.Text);

Open in new window


This works, HOWEVER the output in the memo is not in real-time (i.e. one line each two seconds) BUT once in one step when the console program closes:

console program output in one step when console program terminates
So how can I output the lines from the console program in REAL-TIME?
PeterDelphinAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Kyle AbrahamsSenior .Net DeveloperCommented:
try adding the processMessages After you get your result:

EG:
Form1.mmoOutput.Lines.Add(Result); // <<<<<<<<<<<<<<<<<<< output
Application.ProcessMessages;
PeterDelphinAuthor Commented:
Kyle, I did as you wrote. But it does not work, I still get no real-time output. Only the whole output when the console program terminates.
PeterDelphinAuthor Commented:
I also tried this one:

procedure GetThisDosOutput(const Text: string);
begin
  Form1.mmoOutput.Lines.Add(Text);
end;

procedure TForm1.btnStartConsoleExeClick(Sender: TObject);
begin
  JclSysUtils.Execute(edtConsoleExe.Text, nil, @GetThisDosOutput, False, ppNormal);
end;

Open in new window


However, with this code I always get this compiler error:

[dcc32 Error] GetDosOutput_Master_MainForm.pas(151): E2250 There is no overloaded version of 'Execute' that can be called with these arguments

Maybe someone could provide a working example using JclSysUtils.Execute?
Microsoft Azure 2017

Azure has a changed a lot since it was originally introduce by adding new services and features. Do you know everything you need to about Azure? This course will teach you about the Azure App Service, monitoring and application insights, DevOps, and Team Services.

PeterDelphinAuthor Commented:
There is also another code here: http://delphi.wikia.com/wiki/Capture_Console_Output_Realtime_To_Memo

However, also with this code I get a Delphi XE7 compiler error on the line with OemToAnsi(pBuffer, pBuffer);.

Maybe someone could give me a hint on how to make this code work?
PeterDelphinAuthor Commented:
PeterDelphinAuthor Commented:
There seems to be something wrong with my console program. I tried this code (answer from David Heffernan):

http://stackoverflow.com/questions/25723807/execute-dos-program-and-get-output-dynamically

With this code I still get no real-time output (only when the console program terminates) with my console program (see above), but I do get real-time output with a youtube-downloader console program (https://github.com/rg3/youtube-dl/blob/master/README.md)

WHY?
Bruno BuesserCommented:
Try to replace Sleep(2000) by SoftDelay(2000):

procedure SoftDelay(ADelay: UInt64);
var
  TickCount: UInt64;
begin
  TickCount:=GetTickCount64;
  repeat
    Application.ProcessMessages;
  until GetTickCount64-TickCount > ADelay;
end;

Open in new window

PeterDelphinAuthor Commented:
Even with SoftDelay I don't get real-time output from GetDosOutput_Console_Test with the code of David Heffernan, only when GetDosOutput_Console_Test.exe terminates.
Sinisa VukSoftware architectCommented:
You must flush each writeln in console app.
try
    Writeln('1 of 5');
    Flush(Output);
    Sleep(2000);
    Writeln('2 of 5');
    Flush(Output);
    Sleep(2000);
    Writeln('3 of 5');
    Flush(Output);
    Sleep(2000);
    Writeln('4 of 5');
    Flush(Output);
    Sleep(2000);
    Writeln('5 of 5');
    Flush(Output);
    Sleep(2000);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

Open in new window


or may looks:
procedure MyWriteln(sText: String);
begin
  Writeln(sText);
  Flush(Output);
end;

begin
  try
    MyWriteln('1 of 5');
    Sleep(2000);
    MyWriteln('2 of 5');
    Sleep(2000);
    MyWriteln('3 of 5');
    Sleep(2000);
    MyWriteln('4 of 5');
    Sleep(2000);
    MyWriteln('5 of 5');
    Sleep(2000);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Open in new window


I use this reader:
http://thundaxsoftware.blogspot.co.uk/2012/12/capturing-console-output-with-delphi.html

one note: CRLF from writeln comes into buffer too - so you need to remove it.

Another solution is described in this article (c, but can be ported). It uses own Console/buffer and api function ReadConsoleOutputCharacter.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Geert GOracle dbaCommented:
why look on another site ?
epasquier seems to have stopped posting ... can't blame him, not much activity here anymore compared to before
http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_27460239.html
PeterDelphinAuthor Commented:
Thank you for the solution!

And sorry for the long delay! Was away!
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.