Hi Guys
Not sure if this is the correct forum but;
I have an application that I wrote in mid 2022.
The app is designed for GNU/Linux. It uses TProcess to write to stdin of other processes.
The app requires some minor update and today, after a rebuild using the latest Typhon64 8.60, I discovered that
sending strings via TProcess to external process fails.
Don't know if something changed in later FPC or in Linux libs.
Details of system:
OS: Debian GNU/Linux 12 with latest updates
Code that no longer works since ver 8.21
//
//* Send data to process's STDIN //* nino 24-08-2022
//
if (_iList <> nil) and (_iList.Count > 0) then
begin
Strg := _iList.Text;
Process.Input.Write(Strg[1], Length(Strg));
Process.CloseInput();
end;
//
Full function code include:
//
//* If we don't know the size of the output, we cannot use poWaitOnExit.
//* On Linux the size of output pipe is 2 kB.
//* If the output data is more, we need to read the data.
//* This isn't possible if we are waiting, so we would get a deadlock here.
//* The Wait parameter default value is wsNoWait.
//* Use Wait = wsWait when you need to wait for a process with little output.
//*
//* CommandLine parameters are passed via [_pList]. One parameter per line please!
//*
//* Environment strings, as name/value pairs, are passed to the
//* executable via [_eList] if [_eList] if supplied.
//*
//* Data passed via [_iList] will be passed to the process's STDIN
//*
//* A temp Memorystream is used to buffer the output from STDOUT.
//* A temp Memorystream is used to buffer the output from STDERR.
//*
//* The buffer output is copied into [_sList] if [_sList] if supplied.
//*
//* If the command in [_CommandLine] causes an error, [_sList] will contain
//* the output that would normally go to STDERR.
//*
//* Returns True on command execution success (output from STDOUT)
//* Returns False on command execution failure (output from STDERR)
//
function ExecProcessEx(const _Executable : string; //* NL - 27 Oct 2022
_pList : TStringList = nil; //* CMD Line Param list
_sList : TStringList = nil; //* List to where the buffer output is copied
_eList : TStringList = nil; //* Environment strings to pass, as name/value pairs
_iList : TStringList = nil; //* Data to be passed to the process's STDIN
_Wait : TWaitState = wsNoWait) : Boolean;
const
READ_BYTES = 20480;
var
OutCount : LongInt;
ErrCount : LongInt;
OutBytesRead : LongInt;
ErrBytesRead : LongInt;
Done : Boolean;
Strg : string;
ProcessCMDLine : string;
Process : TProcess;
OutStream : TMemoryStream;
ErrStream : TMemoryStream;
begin
OutStream := TMemoryStream.Create();
ErrStream := TMemoryStream.Create();
try
OutBytesRead := 0;
ErrBytesRead := 0;
Process := TProcess.Create(nil);
try
ProcessCMDLine := _Executable; //* nino 14-05-2011
if _CheckIfShellRequired(ProcessCMDLine, _Executable) then
Process.CommandLine := ProcessCMDLine //* nino 24-10-2014
else
Process.Executable := _Executable;
//* Add Command Line Parameters
if _pList <> nil then
Process.Parameters := _pList; //* nino 27-10-2022
//* Set environment variables
if _eList <> nil then //* nino 13-06-2015
Process.Environment := _eList;
{$ifdef nl_debug}
Writeln(Process.CommandLine);
{$endif}
case _Wait of
wsWait : Process.Options := [poWaitOnExit, poUsePipes];
wsNoWait : Process.Options := [poUsePipes];
end; //* case.
try
//* Run the process.
Process.Execute();
//
//* Send data to process's STDIN //* nino 24-08-2022
//
if (_iList <> nil) and (_iList.Count > 0) then
begin
Strg := _iList.Text;
Process.Input.Write(Strg[1], Length(Strg));
Process.CloseInput();
end;
//
except
Result := False;
_sList.Clear();
_sList.Add('unknown process error [nl_exec.ExecProcessEx()]');
Exit;
end;
Done := False;
//* Accumulate all process output and/or error output.
while not Done do
begin
//* make sure we have room
OutStream.SetSize(OutBytesRead + READ_BYTES);
ErrStream.SetSize(ErrBytesRead + READ_BYTES);
//* try reading it.
OutCount := Process.Output.Read((OutStream.Memory + OutBytesRead)^, READ_BYTES);
ErrCount := Process.Stderr.Read((ErrStream.Memory + ErrBytesRead)^, READ_BYTES);
if OutCount > 0 then
Inc(OutBytesRead, OutCount);
if ErrCount > 0 then
Inc(ErrBytesRead, ErrCount);
if (OutCount = 0) and (ErrCount = 0) then
Done := True;
end;
OutStream.SetSize(OutBytesRead);
ErrStream.SetSize(ErrBytesRead);
//* Copy Stream into sList.
if _sList <> nil then
begin
Result := True;
_sList.LoadFromStream(OutStream);
//* if OutStream is empty, copy ErrStream into [_sList]
if _sList.Count = 0 then
begin
_sList.LoadFromStream(ErrStream);
Result := False;
end;
end;
finally
SafeFreeAndNil(Process);
end;
finally
SafeFreeAndNil(ErrStream);
SafeFreeAndNil(OutStream);
end;
end;