2

I need to know whether stdin or stdout has been redirected for a console application using the latest FPC (v3.0.0)

In the old Turbo Pascal days, I had a function written in assembly that did this. See below:

{ **************************************************************
  * Routine   : RedirectedStd                                  *
  * Purpose   : Return Yes (True) if standard handle is being  *
  *           : redirected.                                    *
  * Note(s)   : Even though handle can take any handle value,  *
  *           : the function will be meaningful only for the   *
  *           : standard input, standard output, and standard  *
  *           : error.  It will, however, be True for any      *
  *           : handle that does NOT point to the console.     *
  *           : (Besides, this is what it actually checks for.)*
  *           : Make sure handle belongs to an open file or    *
  *           : you will get wrong answer (check IOResult).    *
  ************************************************************** }
function RedirectedStd(handle: Word): Boolean; assembler;
const
  DEVICE       = $0080;
  FASTCONSOLE  = $0010;
  CONSOUT      = $0002;
  CONSIN       = $0001;
asm
  mov     InOutRes,0
  mov     ax,$4400       { IOCTL svc, get device information }
  mov     bx,handle
  int     $21            { result in DX }
  mov     ax,1           { assume function is True }
  jc      @Error         { got error with code in AX }
  test    dx,DEVICE
  jz      @Out
  test    dx,FASTCONSOLE
  jz      @Out
  test    dx,CONSOUT
  jz      @Out
  test    dx,CONSIN
  jz      @Out
  xor     ax,ax          { function is False }
  jmp     @Out
@Error:
  mov     InOutRes,ax
@Out:
end; { RedirectedStd }

This syntax is not valid for the FPC assembler. I tried my luck with the following variant which although compiles OK it crashes:

function RedirectedStd(handle: Word): Boolean; assembler;
label Error,Done;
const DEVICE       = $0080;
      FASTCONSOLE  = $0010;
      CONSOUT      = $0002;
      CONSIN       = $0001;
asm
          movw      $0,InOutRes
          movw      $4400,%ax           { IOCTL svc, get device information }
          movw      handle,%bx
          int       $21                 { result in DX }
          movw      $1,%ax              { assume function is True }
          jc        Error               { got error with code in AX }
          test      DEVICE,%dx
          jz        Done
          test      FASTCONSOLE,%dx
          jz        Done
          test      CONSOUT,%dx
          jz        Done
          test      CONSIN,%dx
          jz        Done
          xor       %ax,%ax             { function is False }
          jmp       Done
Error:    movw      %ax,InOutRes
Done:
end; { RedirectedStd }

(Not sure if my conversion is equivalent.)

Any ideas?

EDIT: Based on accepted answer which gave me enough direction to figure out the solution, I came up with the following drop-in replacement for my original routine:

function RedirectedStd(handle: Word): Boolean; {$ifndef WINDOWS} unimplemented; {$endif}
begin
  RedirectedStd := False;
  {$ifdef WINDOWS}
  case handle of
    0: RedirectedStd := GetFileType(GetStdHandle(STD_INPUT_HANDLE)) <> FILE_TYPE_CHAR;
    1: RedirectedStd := GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)) <> FILE_TYPE_CHAR;
    2: RedirectedStd := GetFileType(GetStdHandle(STD_ERROR_HANDLE)) <> FILE_TYPE_CHAR;
  end;
  {$endif}
end; { RedirectedStd }

Oh, and you need to do Uses Windows;

tonypdmtr
  • 3,037
  • 2
  • 17
  • 29
  • Possible duplicate of [How can I determine whether Console.Out has been redirected to a file?](http://stackoverflow.com/questions/743885/how-can-i-determine-whether-console-out-has-been-redirected-to-a-file) – David Heffernan Nov 20 '16 at 20:55
  • @DavidHeffernan: Not really. I'm looking for a FreePascal solution. – tonypdmtr Nov 20 '16 at 22:44
  • Call the winapi functions in the top answer. Or find one of the countless other dupes that say the same thing. Here's one http://stackoverflow.com/questions/9021916/how-do-i-check-if-my-delphi-console-app-is-redirected-to-a-file-or-pipe there are many many more – David Heffernan Nov 20 '16 at 22:46
  • Your implementation fails to recognize, when an I/O stream is redirected to another character device (e.g. a printer: `a.exe > LPT1`). A common solution to this is to call [GetConsoleMode](https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167.aspx), when `GetFileType` returns `FILE_TYPE_CHAR`. If the call fails, the stream has been redirected to another character device. For what it's worth, that's how .NET implements [IsHandleRedirected](https://referencesource.microsoft.com/#mscorlib/system/console.cs,f108bd7fd3c5ebdc). – IInspectable Nov 21 '16 at 01:13

1 Answers1

3

Use

GetFileType(GetStdHandle(STD_OUTPUT_HANDLE));

for stdout. It should be obvious what to do for stdin.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Good enough. I had to do a bit of more work to figure out how the returned values are used, but got it working. I updated my question to include the working function. – tonypdmtr Nov 20 '16 at 23:40
  • 2
    It's all documented you know. Should be clear from that. Don't use magic constants. – David Heffernan Nov 21 '16 at 04:38