Determine where STDOUT is redirected

TCC has the _stdout internal variable,
it's purpose being to return 1 if STDOUT points to the console, or 0 if it has been redirected.

I would like to know where STDOUT is being redirected to.

If there is a method to do this, I have missed it in the help file.

The .btm that I wrote (below) is supposed to tell me,
but it always returns 0.

Here are a few sample runs;
Code:
E:\Utils>stdio.btm
STDIN points to the console
STDOUT points to the console
GetStdHandle: 84
FILE_TYPE: 0
Either the type of the specified file is unknown, or the function failed.

Code:
E:\Utils>stdio > clip9:

E:\Utils>type clip9:
STDIN points to the console
STDOUT is redirected
GetStdHandle: 3296
FILE_TYPE: 0
Either the type of the specified file is unknown, or the function failed.

Code:
E:\Utils>stdio.btm > r:\results.txt

E:\Utils>type r:\results.txt
STDIN points to the console
STDOUT is redirected
GetStdHandle: 3272
FILE_TYPE: 0
Either the type of the specified file is unknown, or the function failed.

Code:
stdio.btm | view
STDIN points to the console
STDOUT is redirected
GetStdHandle: 1188
FILE_TYPE: 0
Either the type of the specified file is unknown, or the function failed.

I would appreciate a review of my code.

Maybe I am not understanding the Win API functions correctly.
Code:
@setlocal
@echo off
::The PowerBASIC WinBase.inc has the following;
::
:: %STD_INPUT_HANDLE  = &HFFFFFFF6???  '(DWORD)-10
:: %STD_OUTPUT_HANDLE = &HFFFFFFF5???  '(DWORD)-11
:: %STD_ERROR_HANDLE  = &HFFFFFFF4???  '(DWORD)-12
::
:: https://learn.microsoft.com/en-us/windows/console/getstdhandle
::
:: Convert -10 to Decimal
set STD_INPUT_HANLDE=%@convert[16,10,FFFFFFF6???]
:: Convert -11 to Decimal
set STD_OUTPUT_HANDLE=%@convert[16,10,FFFFFFF5???]
:: Convert -12 to Decimal
set STD_ERROR_HANDLE=%@convert[16,10,FFFFFFF4???]
::
:: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfiletype
set FILE_TYPE_CHAR=2
set FILE_TYPE_DISK=1
set FILE_TYPE_PIPE=3
set FILE_TYPE_REMOTE=32768
set FILE_TYPE_UNKNOWN=0
::
if %_stdin  eq 1 (echo STDIN points to the console)  else (echo STDIN is redirected)
if %_stdout eq 1 (echo STDOUT points to the console) else (echo STDOUT is redirected)
::
:: Get the Standard Output Handle
::
set hFile=%@winapi[kernel32.dll,GetStdHandle,%STD_OUTPUT_HANDLE]
echo GetStdHandle: %hFile
::
:: Get the File Type value
::
:: NOTE: hFile was missing the preceeding %
:: Ref. https://jpsoft.com/forums/threads/determine-where-stdout-is-redirected.11399/post-64820
set FILE_TYPE=%@winapi[kernel32.dll,GetFileType,%hFile]
echo FILE_TYPE: %FILE_TYPE
switch %FILE_TYPE
  case %FILE_TYPE_CHAR
    echo The specified file is a character file, typically an LPT device or a console.
  case %FILE_TYPE_DISK
    echo The specified file is a disk file.
  case %FILE_TYPE_PIPE
    echo The specified file is a socket, a named pipe, or an anonymous pipe.
  case %FILE_TYPE_REMOTE
    echo Unused.
  case %FILE_TYPE_UNKNOWN
    echo Either the type of the specified file is unknown, or the function failed.
  default
    echo hFile: %hFile
endswitch

endlocal

Joe
 
Last edited:
You omitted a '%' in the last line below.

Code:
set hFile=%@winapi[kernel32.dll,GetStdHandle,%STD_OUTPUT_HANDLE]
echo GetStdHandle: %hFile
::
:: Get the File Type value
::
set FILE_TYPE=%@winapi[kernel32.dll,GetFileType,hFile]
 
Besides that, it works.

Code:
v:\> handles.btm
STDIN points to the console
STDOUT points to the console
GetStdHandle: 92
FILE_TYPE: 2
The specified file is a character file, typically an LPT device or a console.

v:\> handles.btm | grep .
STDIN points to the console
STDOUT is redirected
GetStdHandle: 2576
FILE_TYPE: 3
The specified file is a socket, a named pipe, or an anonymous pipe.

v:\> handles.btm > test.txt & type test.txt
STDIN points to the console
STDOUT is redirected
GetStdHandle: 2616
FILE_TYPE: 1
The specified file is a disk file.
 
At least in the case of a disk file, you can get the name of the file.

Code:
v:\> handles.btm > test.txt & type test.txt
STDIN points to the console
STDOUT is redirected
GetStdHandle: 1284
FILE_TYPE: 1
\\?\D:\work\test.txt
The specified file is a disk file.

I did that with this (260 = MAX_PATH).

Code:
echo %@winapi[kernel32,GetFinalPathNameByHandle,%hFile,buffer,260,0]
 
I have an ImDisk RAMDrive (R:)
Code:
R:\>echo %@fstype[r:]
NTFS

R:\>free r:

 Volume in drive R is unlabeled    Serial number is c062:deee
       2,147,479,552 bytes total disk space
          23,240,704 bytes used
       2,124,238,848 bytes free
                 1.1 % in use

When re-direction is to a disk file on drive R:,
using GetFinalPathNameByHandle,
no results are returned.

Using @FILEHANDLE,
the correct results are returned.
Code:
E:\Utils>stdio.btm > clip9:

E:\Utils>type clip9:
STDIN points to the console
STDOUT is redirected
GetStdHandle : 4356
FILE_TYPE: 1
File Name:
File Name: R:\temp\COUT37e8.JPS
The specified file is a disk file.
E:\Utils>stdio.btm > r:\results.txt

E:\Utils>type r:\results.txt
STDIN points to the console
STDOUT is redirected
GetStdHandle : 2324
FILE_TYPE: 1
File Name:
File Name: R:\results.txt
The specified file is a disk file.

When redirection is to physical disk E:
Code:
E:\Utils>stdio.btm > results.txt && type results.txt
STDIN points to the console
STDOUT is redirected
GetStdHandle : 4512
FILE_TYPE: 1
File Name: \\?\E:\Utils\results.txt
File Name: E:\Utils\results.txt
The specified file is a disk file.

No matter, I will be using the @FILEHANDLE variable function.

Thanks!

Joe
 
Hmmm! I'm getting mixed results. I can't get @FILEHANDLE to work with a handle from @FILEOPEN while GetFinalPathNameByHandle does work. @FILEHANDLE's help specifically says that handles from @FILEOPEN will work. Any ideas?

Code:
v:\> touch /c foo.txt
2023-03-17 11:17:36.787  V:\foo.txt

v:\> echo %@fileopen[foo.txt,r,t]
888

v:\> echo %@filehandle[888]
TCC: (Sys) The handle is invalid.
 "%@filehandle[888]"

v:\> echo %@winapi[kernel32,GetFinalPathNameByHandle,888,buffer,268,0]
\\?\D:\work\foo.txt

v:\>

The handle is valid according to Sysinternals's HANDLE64.

Code:
v:\> handle64 -p %_pid -a | grep -i foo
  378: File  (R--)   D:\work\foo.txt

v:\> echo %@winapi[kernel32,GetFinalPathNameByHandle,%@eval[0x378],buffer,268,0]
\\?\D:\work\foo.txt

v:\> echo %@filehandle[%@eval[0x378]]
TCC: (Sys) The handle is invalid.
 "%@filehandle[888]"
 
Is it the way the file is created?
Code:
E:\Utils>echo. > foo.txt

E:\Utils>set hnd=%@fileopen[foo.txt,r,t] && echo %hnd && echo %@filehandle[%hnd] && echo %@fileclose[%hnd]
1004
E:\Utils\foo.txt
0

The above works for me.

NOTE: Start a new shell before running.
Handles become corrupted when @filehandle errors out.

Joe
 
Further;
Code:
E:\Utils>touch /c foo.txt
2023-03-17 11:55:21.227  E:\Utils\foo.txt

E:\Utils>dir foo.txt

  Directory of  E:\Utils\foo.txt

2023-03-17  11:55               0  foo.txt
                   0 bytes in 1 file and 0 dirs
   
E:\Utils>echo Test >> foo.txt

E:\Utils>dir foo.txt
 
 Directory of  E:\Utils\foo.txt

2023-03-17  11:55               6  foo.txt

E:\Utils>set hnd=%@fileopen[foo.txt,r,t] && echo %hnd && echo %@filehandle[%hnd] && echo %@fileclose[%hnd]
1004
E:\Utils\foo.txt
0

Joe
 
It seems to fail if the file is empty. And why isn't "echo %@fileclose[%hnd]" executed when @FILEHANDLE fails? I am not using the conditional &&.

Code:
v:\> touch /c foo.txt
2023-03-17 12:08:45.514  V:\foo.txt

v:\> set hnd=%@fileopen[foo.txt,r,t] & echo %hnd & echo %@filehandle[%hnd] & echo %@fileclose[%hnd]
1732
TCC: (Sys) The handle is invalid.
 "%@filehandle[1732]"

v:\> echo %@fileclose[1732]
0

v:\> echo test >> foo.txt

v:\> set hnd=%@fileopen[foo.txt,r,t] & echo %hnd & echo %@filehandle[%hnd] & echo %@fileclose[%hnd]
1732
D:\work\foo.txt
0

v:\> > foo.txt

v:\> set hnd=%@fileopen[foo.txt,r,t] & echo %hnd & echo %@filehandle[%hnd] & echo %@fileclose[%hnd]
1780
TCC: (Sys) The handle is invalid.
 "%@filehandle[1780]"

v:\>
 
In the following code, using the ON ERROR command closes the file, when @FILEHANDLE fails;
Code:
@setlocal
@echo off
on error goto Problem
touch /c foo.txt
set hnd=%@fileopen[foo.txt,r,t]
echo        hnd: %hnd
echo filehandle: %@filehandle[%hnd]
echo  fileclose: %@fileclose[%hnd]
endlocal
quit

:Problem
echo  fileclose: %@fileclose[%hnd]
handle.exe -p %_pid | ffind /t"foo.txt"
quit
Return

As only commands return an exit code,
this is the only way that I know of to trap an error from a function.

Ref: Handle.exe

Added: I should really have the ON ERROR line directly before the @filehandle line,
since that is where we know the error will occur.
Still, it works as-is.

Joe
 
FWIW, I used to use an IMDISK ramdrive (started at system startup by a scheduled task) but it really didn't seem to speed anything up. Now I start/stop it on-demand with these commands, issued elevated.

Code:
imdisk -a -o awe -s 1G -m Z: -p "/fs:ntfs /q /y"
imdisk -d -m z:
 

Similar threads