Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!

Determine where STDOUT is redirected

Aug
1,957
71
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

Back
Top