[fpc-pascal] TProcess problem

Seth Grover sethdgrover at gmail.com
Wed Nov 26 17:55:00 CET 2008


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



More information about the fpc-pascal mailing list