Sign in Join

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

Mailing lists / fpc-devel / 2017-02

Standard in/out/error of Windows GUI apps

1 message · 1 person ·

Reply by email Subscribe to this list Your subscription

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

Henry Vermaak

Hello,

Recently a customer asked whether he could get a GUI version of one of our console apps because the flashing console is annoying him. Fair enough, I thought, this should be easy. We do it with our C utilities already.

I know that a writeln() in a GUI app crashes, from when someone left a stray writeln() in FPC 3.0.x and our app started crashing when looking for updates online. Can someone explain to me what the use of this is? If you know that a handle doesn't exist, why not just redirect the output to 'nul'?

I set out to do this myself, when I discovered that fpc tries to show anything that you write to stdout/stderr in a messagebox. It does this even when the handles are being redirected (to e.g. pipes) by the calling application. Relevant code in syswin.inc (procedure SysInitStdIO):

if not IsConsole then
 begin
  AssignError(stderr);
  AssignError(StdOut);
  Assign(Output,'');
  Assign(Input,'');
  Assign(ErrOutput,'');
 end
...
...

Showing writeln output in a messagebox is questionable, but it doesn't even do it for Output and ErrOutput. The documentation says they are aliases for stdout/stderr? So not only does it ignore whichever possibly valid handles in Std{Output|Input|Error}Handle, but it also leaves ErrOutput pointing to StdOutputHandle.

For anyone interested, this is what I had to do so that a command like "app > stdout.txt 2> stderr.txt" behaves as expected:

{$ifdef windows}
function CanOpenHandle(AHandle: THandle): Boolean;
begin
  Result := GetFileType(AHandle) in [FILE_TYPE_DISK, FILE_TYPE_PIPE, FILE_TYPE_CHAR];
end;
procedure RedirectStdIO(var AFile: Text; AHandle: THandle);
var
  CanOpen: Boolean;
begin
  CanOpen := CanOpenHandle(AHandle);
  AssignFile(AFile, IfThen(CanOpen, '', 'nul'));
  if AHandle = StdOutputHandle then begin
    Rewrite(AFile);
  end else if AHandle = StdErrorHandle then begin
    Rewrite(AFile);
    { Rewriting a file with a '' name will use StdOutputHandle, but we want
      StdErrorHandle, so hack it in. }
    if CanOpen then
      TextRec(AFile).Handle := AHandle;
  end else if AHandle = StdInputHandle then begin
    Reset(AFile);
  end;
end;
procedure RedirectWinGUIStdIO;
begin
  if IsConsole then
    Exit;
  RedirectStdIO(StdOut, StdOutputHandle);
  RedirectStdIO(Output, StdOutputHandle);
  RedirectStdIO(StdErr, StdErrorHandle);
  RedirectStdIO(ErrOutput, StdErrorHandle);
  RedirectStdIO(Input, StdInputHandle);
end;
{$endif}

Then call RedirectWinGUIStdIO at the start. I hope it works for all possibilities, my Windows skills are lacking.

Henry

Reply by email