Delphi – How in Delphi 2009 redirect console (stin, sterr)

delphidelphi-2009

I try several samples in the internet and none of them work – the scripts are not executed- (maybe because are for pre Delphi 2009 unicode?).

I need to run some python scripts and pass arguments to them, like:

python "..\Plugins\RunPlugin.py" -a login -u Test -p test

And capture the output to a string & the errors to other.

This is what I have now:

procedure RunDosInMemo(DosApp:String; var OutData: String);
var
  SA: TSecurityAttributes;
  SI: TStartupInfo;
  PI: TProcessInformation;
  StdOutPipeRead, StdOutPipeWrite: THandle;
  WasOK: Boolean;
  Buffer: array[0..255] of Char;
  BytesRead: Cardinal;
  WorkDir: string;
  Handle: Boolean;
begin
  OutData := '';
  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 or CREATE_UNICODE_ENVIRONMENT;
      wShowWindow := SW_HIDE;
      hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin
      hStdOutput := StdOutPipeWrite;
      hStdError := StdOutPipeWrite;
    end;
    WorkDir := 'C:\';
    Handle := CreateProcess(nil, PChar(DosApp),
                            nil, nil, True, 0, nil,
                            PChar(WorkDir), SI, PI);
    CloseHandle(StdOutPipeWrite);
    if Handle then
    begin
      try
        repeat
          WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
          if BytesRead > 0 then
          begin
            Buffer[BytesRead] := #0;
            OutData := OutData + String(Buffer);
          end;
        until not WasOK or (BytesRead = 0);
        WaitForSingleObject(PI.hProcess, INFINITE);
      finally
        CloseHandle(PI.hThread);
        CloseHandle(PI.hProcess);
      end;
    end else begin
      raise Exception.Create('Failed to load python plugin');
    end;
  finally
    CloseHandle(StdOutPipeRead);
  end;
end;

Best Solution

I'm not certain the WaitForSingleObject is the way to go... I think its better to loop with GetExitCodeProcess(pi.hProcess,iExitCode) until iExitCode <> STILL_ACTIVE and then check for data on each pass through the loop.

The code as written does not operate under Delphi 2007 either, so its not a Delphi 2009 unicode issue.

Changing your inner loop to the following works:

if Handle then
begin
  try
    repeat
      WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
      for ix := 0 to BytesRead-1 do
        begin
          OutData := OutData + AnsiChar(Buffer[ix]);
        end;
      GetExitCodeProcess(pi.hProcess,iExit);
    until (iExit <> STILL_ACTIVE);
  finally
    CloseHandle(PI.hThread);
    CloseHandle(PI.hProcess);
  end;

I made the following corrections/additions to the local variables:

Buffer: array[0..255] of byte;
iExit : Cardinal;
IX : integer;

I also moved the CloseHandle(StdOutPipeWrite) just before the close of the StdOutPipeRead.