Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2008-11

TProcess problem

1 message · 1 person ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Seth Grover

I'm familiarizing myself with TProcess, so I decided to write a simple routine which executes a command and puts STDOUT/STDERR in a stringlist:

-------------------------
procedure ExecCommand(const command : string;
                      const stdout : TStringList;
                      const stderr : TStringList;
                      const timeoutSec : integer;
                      out   exitStatus : integer);
const
  READ_BYTES = 2048;
var
 P: TProcess;
 nOut : LongInt;
 nErr : LongInt;
 StdOutput : TMemoryStream;
 StdOutBytesRead: LongInt;
 StdError : TMemoryStream;
 StdErrBytesRead: LongInt;
 startTime : TDateTime;
begin
  exitStatus := -1;
  StdOutput := TMemoryStream.Create;
  StdError := TMemoryStream.Create;
  StdOutBytesRead := 0;
  StdErrBytesRead := 0;
  try
    P := TProcess.Create(nil);
    try
      P.CommandLine := command;
      P.Options := [poUsePipes];
      startTime := now;
      P.Execute;
      { read stderr and stdout while the process runs }
      while P.Running do begin
        StdOutput.SetSize(StdOutBytesRead + READ_BYTES);
        StdError.SetSize(StdErrBytesRead + READ_BYTES);
        nOut := P.Output.Read((StdOutput.Memory + StdOutBytesRead)^,
READ_BYTES);
        if nOut > 0 then begin
          Inc(StdOutBytesRead, nOut);
        end;
        nErr := P.Stderr.Read((StdError.Memory + StdErrBytesRead)^, READ_BYTES);
        if nErr > 0 then begin
          Inc(StdErrBytesRead, nErr);
        end;
        if (nErr = 0) and (nOut = 0) then Sleep(100);
        if (timeoutSec > 0) and (SecondsBetween(now, startTime) >
timeoutSec) then begin
          P.Terminate(exitStatus);
          raise Exception.Create('Execution timed out');
        end;
      end;
      exitStatus := P.ExitStatus;
      { read what's left over in stdout }
      repeat
        StdOutput.SetSize(StdOutBytesRead + READ_BYTES);
        nOut := P.Output.Read((StdOutput.Memory + StdOutBytesRead)^,
READ_BYTES);
        if nOut > 0 then begin
          Inc(StdOutBytesRead, nOut);
        end;
      until nOut <= 0;
      { read what's left over in stderr }
      repeat
        StdError.SetSize(StdErrBytesRead + READ_BYTES);
        nErr := P.Stderr.Read((StdError.Memory + StdErrBytesRead)^, READ_BYTES);
        if nErr > 0 then begin
          Inc(StdErrBytesRead, nErr);
        end;
      until nErr <= 0;
      StdOutput.SetSize(StdOutBytesRead);
      if Assigned(stdout) then begin
        stdout.LoadFromStream(StdOutput);
      end;
      StdError.SetSize(StdErrBytesRead);
      if Assigned(stderr) then begin
        stderr.LoadFromStream(StdError);
      end;
    finally
      FreeAndNil(StdOutput);
      FreeAndNil(StdError);
      FreeAndNil(P);
    end;
  except
    on E : Exception do begin
      exitStatus := -1;
      if Assigned(stderr) then begin
        stderr.Add(E.Message);
      end;
    end;
  end;
end;
-------------------------

Everything works fine unless the output to STDOUT of the command exceeds 65K (I'm testing it by running "cat /path/to/some/file" as the command). When this happens, the P.Stderr.Read inside the while loop just hangs and never returns.

What am I doing wrong?

-SG

========================
Computer over. Virus = very yes.

Seth Grover
sethdgrover[at]gmail[dot]com