[How-To] Distinguish between console and terminal (PowerShell hybrid)

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
aGerman
Expert
Posts: 3934
Joined: 22 Jan 2010 18:01
Location: Germany

[How-To] Distinguish between console and terminal (PowerShell hybrid)

#1 Post by aGerman » 29 Aug 2020 07:03

Lately I found a weird workaround of how to figure out if a script runs in a console window or in a terminal emulator.
The comments in the code list the applications I've already tested.

Code: Select all

@echo off &setlocal

:: Returns 0 if %isterminal% is executed in a terminal emulator.
:: - Windows Terminal
:: - ConEmu
:: - Alacritty
:: - Cmder
:: - Terminus
:: - Hyper
:: Returns 1 if executed in a console.
:: - Windows Console Host (usual console window)
:: - Console2
:: - ConsoleZ
set isterminal=powershell.exe -nop -ep Bypass -c ^"$c=Add-Type -Name pInv -PassThru -MemberDefinition '^
%=% [DllImport(\"user32.dll\")] public static extern IntPtr SendMessageW(IntPtr hWnd,int Msg,IntPtr wParam,IntPtr lParam);^
%=% [DllImport(\"kernel32.dll\")] public static extern IntPtr GetConsoleWindow(); ';^
%=% exit [int]($c::SendMessageW($c::GetConsoleWindow(),($WM_GETICON=0x7F),[IntPtr]::Zero,[IntPtr]::Zero) -ne [IntPtr]::Zero);^"

%isterminal% && (
  echo is a terminal
) || (
  echo is a console
)

pause
[OT]
I'm not sure how useful this actually is. Probably not of much interest for scripts but rather for apps that use the console API which might behave differently in a terminal. However, based on the current reactions found in this issue ...
https://github.com/microsoft/terminal/issues/7434
... I don't believe Microsoft is willing to implement an API for this purpose. So, for those of you who need the C equivalent of the code above, here you go

Code: Select all

#include <windows.h>

int main(void)
{
  return 0 != SendMessageW(GetConsoleWindow(), WM_GETICON, 0, 0);
}
[/OT]

Steffen

// EDIT
Based on the comments of Eureka! I tried to combine his command lines for an alternative using WMIC:

Code: Select all

@echo off &setlocal

for /f "tokens=2 delims==" %%i in (
  'wmic process WHERE ^"CommandLine LIKE '%comspec:\=\\% /c wmic process WHERE \^"CommandLine LIKE%%'^" get ParentProcessId /value'
) do for /f %%j in ("%%i") do 2>nul wmic process WHERE "ParentProcessId=%%j" get Caption|>nul findstr /i "conhost.exe" && (
  echo Console
) || (
  echo No Console
)

pause

jfl
Posts: 140
Joined: 26 Oct 2012 06:40
Location: Saint Hilaire du Touvet, France
Contact:

Re: [How-To] Distinguish between console and terminal (PowerShell hybrid)

#2 Post by jfl » 03 Sep 2020 11:58

Thanks Steffen.

I gave it a try, and it's working fine on my system.
Still, it's a bit risky to rely on an undocumented API side effect.

My own tools work fine in both the old console, and the new terminal, except for a few minor differences with the chars.exe tool: It is confused by the terminal interpretation of additional control codes:

In console:

Code: Select all

C:\JFL\Temp>chcp
Active code page: 437

C:\JFL\Temp>chars
    00    10 ►  20    30 0    40 @  50 P  60 `  70 p
    01 ☺  11 ◄  21 !  31 1    41 A  51 Q  61 a  71 q
    02 ☻  12 ↕  22 "  32 2    42 B  52 R  62 b  72 r
    03 ♥  13 ‼  23 #  33 3    43 C  53 S  63 c  73 s
    04 ♦  14 ¶  24 $  34 4    44 D  54 T  64 d  74 t
    05 ♣  15 §  25 %  35 5    45 E  55 U  65 e  75 u
    06 ♠  16 ▬  26 &  36 6    46 F  56 V  66 f  76 v
    07    17 ↨  27 '  37 7    47 G  57 W  67 g  77 w
    08    18 ↑  28 (  38 8    48 H  58 X  68 h  78 x
    09    19 ↓  29 )  39 9    49 I  59 Y  69 i  79 y
    0A    1A    2A *  3A :    4A J  5A Z  6A j  7A z
    0B ♂  1B ←  2B +  3B ;    4B K  5B [  6B k  7B {
    0C ♀  1C ∟  2C ,  3C <    4C L  5C \  6C l  7C |
    0D    1D ↔  2D -  3D =    4D M  5D ]  6D m  7D }
    0E ♫  1E ▲  2E .  3E >    4E N  5E ^  6E n  7E ~
    0F ☼  1F ▼  2F /  3F ?    4F O  5F _  6F o  7F ⌂
In terminal:

Code: Select all

C:\JFL\Temp>chcp
Active code page: 437

C:\JFL\Temp>chars
    00   10 ►  20    30 0    40 @  50 P  60 `  70 p
    01 ☺  11 ◄  21 !  31 1    41 A  51 Q  61 a  71 q
    02 ☻  12 ↕  22 "  32 2    42 B  52 R  62 b  72 r
    03 ♥  13 ‼  23 #  33 3    43 C  53 S  63 c  73 s
    04 ♦  14 ¶  24 $  34 4    44 D  54 T  64 d  74 t
    05 ♣  15 §  25 %  35 5    45 E  55 U  65 e  75 u
    06 ♠  16 ▬  26 &  36 6    46 F  56 V  66 f  76 v
    07    17 ↨  27 '  37 7    47 G  57 W  67 g  77 w
    08    18 ↑  28 (  38 8    48 H  58 X  68 h  78 x
    09    19 ↓  29 )  39 9    49 I  59 Y  69 i  79 y
    0A    1A    2A *  3A :    4A J  5A Z  6A j  7A z
    0B
  1B B +  3B ;    4B K  5B [  6B k  7B {
    0C
  1C ∟  2C ,  3C <    4C L  5C \  6C l  7C |
    0D    1D ↔  2D -  3D =    4D M  5D ]  6D m  7D }
    0E   1E ▲  2E .  3E >    4E N  5E ^  6E n  7E ~
    0F   1F ▼  2F /  3F ?    4F O  5F _  6F o  7F
The codepage.exe tool displays all code pages correctly on both sides.

But it has a minor problem due to the console API:

In the old console, it displays the correct font:

Code: Select all

C:\JFL\Temp>codepage
Current console code page: 437 = OEM - United States
Default console code page: 437 = OEM - United States
System code page: 1252 = ANSI - Latin I
Console font: [TrueType] Liberation Mono

C:\JFL\Temp>
But in the new terminal, I have a doubt about the font name: Shouldn't it be "Cascadia Mono" by default, not Consolas?)

Code: Select all

C:\JFL\Temp>codepage
Current console code page: 437 = OEM - United States
Default console code page: 437 = OEM - United States
System code page: 1252 = ANSI - Latin I
Console font: [TrueType] Consolas

C:\JFL\Temp>

aGerman
Expert
Posts: 3934
Joined: 22 Jan 2010 18:01
Location: Germany

Re: [How-To] Distinguish between console and terminal (PowerShell hybrid)

#3 Post by aGerman » 03 Sep 2020 15:25

jfl wrote:
03 Sep 2020 11:58
Still, it's a bit risky to rely on an undocumented API side effect.
I agree. And this is the reason why the issue has been filed - just to get feedback from the terminal team whether or not there is a recommended way. But as you can see they don't even understand why this could be useful.

There is a difference of how the window is structured. In a console you have the screen buffer which might be larger than the window. You can scroll down to the part of the screen buffer which is not yet used. You might also be able to scroll to the right if the screen buffer is wider than the window.
In the terminal you have the view port which is the visible part of the window. This view port is dynamically extended at the bottom if necessary, while old content disappears on the top into the history. And you never have a horizontal scroll bar. So, the view port can't be wider than the window and long lines are always wrapped. You can't scroll into the history using the console API. And setting the cursor to coordinates 0,0 will set it to the top left corner of the view port instead of scrolling up to the top left corner of the history. And if you try to emulate the behaviour of the CLS command using the console API you'll also only clear the view port while the history is untouched.
These are my experiences so far :)

Steffen

Eureka!
Posts: 102
Joined: 25 Jul 2019 18:25

Re: [How-To] Distinguish between console and terminal (PowerShell hybrid)

#4 Post by Eureka! » 03 Sep 2020 17:21

I no longer have Terminal installed, so can't test, but checking for child-process conhost.exe of the current CMD.exe should do it?

Code: Select all

+ T:\>wmic process WHERE "CommandLine like 'wmic  process WHERE \"CommandLine like%%'" get ParentProcessId /format:list


ParentProcessId=2956




+ T:\>wmic process WHERE "ParentProcessId=2956" get Caption 2>nul| findstr /i "conhost.exe" >nul && echo Console || echo No Console
Console

+ T:\>

(Can't remember what the predecessor of conhost.exe on XP was called, but IIRC that was also a child process of CMD.

aGerman
Expert
Posts: 3934
Joined: 22 Jan 2010 18:01
Location: Germany

Re: [How-To] Distinguish between console and terminal (PowerShell hybrid)

#5 Post by aGerman » 04 Sep 2020 11:23

Seems to work (at least for the Windows Terminal). Although it's even more weird. If I run the second command line using the parent PID but without the findstr filter it reports that it belongs to wmic.exe. But, seriously, the wmic process should neither be the parent nor should it still be alive if I run the second command line. So, I don't know how reliable this is :lol:
Anmerkung 2020-09-04 192147.png
Anmerkung 2020-09-04 192147.png (33.09 KiB) Viewed 316 times

Eureka! wrote:
03 Sep 2020 17:21
(Can't remember what the predecessor of conhost.exe on XP was called, but IIRC that was also a child process of CMD.
Maybe csrss.exe. But I can't test it anymore.

Steffen

Eureka!
Posts: 102
Joined: 25 Jul 2019 18:25

Re: [How-To] Distinguish between console and terminal (PowerShell hybrid)

#6 Post by Eureka! » 04 Sep 2020 15:39

The first command:
CMD kicks of WMIC.exe so that will be a child process of CMD.
The first command searches for that specific WMIC process and reports it's parent-process ID (that is the ProcessID of CMD.exe)

The second command:
This one reports all child-processes of CMD.exe (and checks if conhost.exe is one of them)
In case of console, that will be conhost.exe.
But it will also report WMIC.exe. Not the one from the first command, but from the wmic process WHERE "ParentProcessId=9148" command.


So the WMIC.exe caption you are seeing comes from the second command.
csrcc.exe
Yes, that's the one! You have a better memory than I have :)

aGerman
Expert
Posts: 3934
Joined: 22 Jan 2010 18:01
Location: Germany

Re: [How-To] Distinguish between console and terminal (PowerShell hybrid)

#7 Post by aGerman » 04 Sep 2020 17:00

Yeah, now that I've read it I got it. For whatever reason I failed to understand the ParentProcessId in the second command. (probably just the copypasta syndrome :lol: )

Post Reply