-
Notifications
You must be signed in to change notification settings - Fork 8.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DISCUSSION: Is my app running in Terminal? #7434
Comments
Could you clarify? What exactly are you trying to do? |
Looks like somebody is trying to set the icon by literally telling the window manager to replace it. Hm. |
I'm trying to find out if my app is running in Windows Terminal. I don't care about the icon. If I'm in a console, that evaluates to FALSE; in Terminal to TRUE. |
If you have a reason to detect if you are running inside terminal, we should probably have the discussion as to why you want to detect that. Any behavior you believe to be Terminal-specific is not, I promise you, and your application will do the wrong thing. |
Didn't you (a while back) give me a list of Win32 API console functions that wouldn't work as expected in Terminal. I suppose much behavior IS Terminal-specific and I don't want my app doing the wrong thing. |
Ah, those functions you should avoid calling in general, not just "in the Windows Terminal". Those are functions that'll work less well in any ConPTY session. This includes the Windows Terminal, Very specifically, |
I wish I saw the bigger picture. I don't even know what ConPTY is. For me Windows Terminal is the only alternative to conhost. I don't know how to re-phrase my question. |
Don't worry about it, let me help however I can.
In short: The rest of the terminal world (*nix) uses streams of characters to have client applications interact with the Terminal. On windows however, client apps use the Console API to interact with the "terminal" (conhost.exe). The Console API however is terrible and full of poorly documented, poorly supported APIs. Plus, it only works on Windows, so it's hard for commandline client apps to target both Windows and *nix OSes. Enter ConPTY - Conpty is a magic layer that translates all the console API calls to streams of characters, so that they could be used by any terminal emulator. This means that terminal no longer needs to understand the ins and outs of the console API - conpty will do that bit for them. Conpty is what powers the Terminal - fundamentally, the Terminal could be hooked up to a linux commandline client app, and work just fine, via a stream of characters. This is a very broad overview, but should help explain the situation. |
Thanks. For me, Terminal is just a way to run command interpreters in Windows. It has features that the Windows console lacks. My preferred command interpreter is "TCC" (grandson of "4DOS", still from JP Software). I write plugins (DLLs) to extend its capabilities. Having used the console APIs for a long time I'm pretty comfortable with them. Many things don't work in Terminal (and never will) and I'd like one of my plugins (dedicated to console-related things) to decline being loaded if TCC is running in Terminal. Hence, my question. The SendMessage was just a gimmick. I used WM_GETICON because I saw it sent to (Terminal's version of) GetConsoleWindow(). And FWIW, WindowsTerminal.exe crashed on me a few times after being spied on with Spy++. That is not reliably reproducible. |
What kinds of things are you doing specifically? I'd love to hear more about your use case |
Hmmm! I could go on for a long time. The plugin interface allows the plugin to export COMMANDs, internal _VARIABLES, variable @functions, and the address of a keystroke-handling function. In all, I have more than 300 items that do quite a variety of things. Here are a very few examples. CONSIZE - set any of screen buffer size, console window size (characters), and console window position (absolute, relative, and nine canonical centered positions) CONIMAGE/CONRESTORE - save/restore the console sceeen buffer (text and attributes), SETICON - set the console's icon (to any from a DLL or EXE) v:> echo %@iCons[c:\windows\system32\shell32.dll] v:> echo %@keytime[hkcu\environment] v:> echo %@up[v] v:> echo %@up[d] v:> echo %_ruler My keyhandler's most useful function is bound to Ctrl-Del. Leaving the current prompt fixed, it erases the text above, one line at a time, while scrolling the text further-above down (to just above the command line). There is no viewport/history distinction as in Terminal so I can go right up to the top of the console screen buffer. This is very useful if I'd like to execute a sequence of commands (say to be copied and pasted into a post). If I botch one I can erase it and its output and try again. Ctrl-Shift-Del does the same 1 page at a time. I already have a rudimentary 4WT.DLL plugin which implements Ctrl-Del = delete upwards (as best it can ... pretty easy with VT control sequences). |
I might have gotten carried away there. If you only wanted to hear about things that are or might be problematic in Terminal ... v:> help _curchar I don't know if I can do that if running in Terminal. Does GetCurrentConsoleFontEx() work? I might also need Terminal's padding????? |
M. Fatica: http://jdebp.uk./FGA/capture-console-win32.html may help a little bit with the concepts and architecture. Other people: See https://jpsoft.com/forums/threads/detect-windows-terminal.10484/ for some more context. |
Shouldn't the question be answered as, simply, check if $TERM is set, and if you don't want to handle the particularities of terminals, pretend all terminals comply to the modern standard of "emulate xterm-256color", and emit the proper sequences? |
@Diablo-D3 no, and I'll try to share more details:
I don't believe that users should have to do a bunch of arcane configuration before programs can work reasonably (on Windows More importantly, I don't want to handle all the support requests when Windows users attempt to use a Windows console program like Clink and right out of the box it doesn't work at all, simply because the Windows users are using Windows and thus don't have |
You're right in that Windows users are unfamiliar with $TERM. Microsoft chose not to implement this 30+ years ago, and Microsoft customers have been paying for that oversight ever since. However, terminal programs in general are "arcane"; the average user on any OS (even Linux, nowadays) are going to be unfamiliar with terminals. |
Which, on Windows 10+, supports a whole host of control sequences... which muddies the water significantly. |
Almost. But Clink uses IAT hooking to extend CMD.exe. And Clink includes a built in terminal emulator for when it's running in a host that lacks support for escape sequences. And some terminal GUI hosts such as ZConsole just pass escape sequences to the underlying console. Clink needs to know a little about the environment in which it's running.
It is the only way for a program to infer whether it is running inside Microsoft Terminal. Clink needs a reliable way to detect that.
No. How did you reach that conclusion?
It is not "user error" to start Windows Terminal, then start a GUI app from WT, then start a console program from the GUI app. That is a perfectly reasonable thing for a user to do, and it indeed happens.
My point is that Clink wants to provide a seamless experience for users, and that there is no way to do so. It seems you believe that programs should not do that, and that users should not use shells without both configuring the shell and configuring the terminal in advance. I can respect that viewpoint, but I cannot require my program's users to do all that. Especially when there is no documentation or system in place for that on Windows. |
The expectation is mistaken:
On Windows, there are many ways for a console program to run in a new console window. There are APIs for controlling that, the |
With specific regard to a shell wanting to know where the Windows Terminal user interface is ... I revisit this every now and then. I'm using the process ID of GetConsoleWindow() and a Toolhelp32Snapshot to find the parent process of OpenConsole.exe. Then I use EnumWindows() to find a CASCADIA_HOSTING_WINDOW_CLASS window belonging to that parent. It works, at least to my satisfaction, but it's a bit cumbersome. Could you make the handle of the CASCADIA_HOSTING_WINDOW_CLASS window more readily available? One thought is to put it in the value of the WT_SESSION environment variable (along with the GUID which is (AFAICT) useless to the shell.
A shell might be looking for the existence of that variable anyway. If it's value had more useful information in it, that would be great. Am I wrong about the GUID? Is it of any value to the shell (or to anyone)? |
Stop worrying about it. It's pretty obvious that there will be no support from the Terminal folks on this issue. |
What's with the whole stackoverflow attitude of "you're not supposed to do that"? Setting custom window sizes, screen buffers have been a thing on cmd since the dark ages. For an OS that touts itself so much on backward compatibility that it goes out of its way to keep even the rarest of things working the same way, this is such a stupid thing to do. Let us have a way to detect if the script is running in Windows Terminal, we're not asking you to add features from conhost. |
I archived my old termproc repo and created a new one to reflect the new process model of Windows Terminal v. 1.18 and its feature to move tabs between windows. The new code can be found in my termwnd repo for those who are interested in. |
I have a similar conundrum - not being able to detect whether ANSI sequences are supported (for, among other things, colored output and line movement) which boils down to checking if I'm running inside Use-case@setlocal EnableExtensions & if "%DEBUG%"=="" echo off
call :query_state response "0c" c
if defined response (
set /p="%response:*[=^[[%"
(echo()
) <nul
exit /b 0
:query_state (out response: string, query: string, terminator: char, device: string = "con")
setlocal DisableDelayedExpansion
if not "%~4"=="" (
set "device=%~4"
) else (
set "device=con"
)
for /f "delims=" %%c in (
'""%SystemRoot%\system32\forfiles.exe"
/p "%~dp0." /m "%~nx0" /c ""%ComSpec%" /d /q /c @echo(0x1B[""'
) do (
set "CSI=%%c"
)
set /a "index=0"
set "response="
:query_state_unpack
set "character="
set /p="%CSI%%~2" <nul >"%device%"
for /l %%_ in (1, 1, %index%) do (pause) <"%device%" >nul
for /f "skip=1 tokens=1 delims=* eol=" %%c in (
'""%SystemRoot%\system32\replace.exe" ? . /w /u <"%device%""'
) do (
set "character=%%c"
)
set /a "index+=1"
set "response=%response%%character%"
if defined character if not "%character%"=="%3" (
goto :query_state_unpack
)
endlocal & set "%~1=%response%" & goto :EOF To help with that I've cooked the following algorithm; no idea whether it's suitable for all scenarios, but I'd be happy to receive any feedback:
|
@mataha It sounds like you're checking whether there's a conhost.exe that can be identified as either the parent or child of the current cmd.exe, and concluding that means escape sequences are supported. A few examples where the approach doesn't work (there are more):
Looking for conhost.exe has a low enough correlation to escape sequence support that I wouldn't recommend to pursue trying to expand on it. I'd recommend to find a different approach entirely. With that said, if you like the results it achieves in the scenarios that your code runs in, then that's probably what's important for your own purposes. It doesn't generalize well, though. |
For Clink (which hooks into cmd.exe and runs within the cmd.exe process itself) the following is what I'm currently exploring/refining for determining whether cmd.exe is running in Windows Terminal. The intent, of course, is to stop looking at It's running in Windows Terminal if any of the following are true:
(Edited to clarify about OpenConsole.exe.) |
To be honest, I haven't followed the discussion from the beginning so please ignore me if I misunderstood this, but isn't the correct question to ask (if anything) "Am I running under ConPTY?" and not really about Windows Terminal at all? After all, the issues mentioned here aren't really specific to Windows Terminal, but rather for anyone using ConPTY right? As such, couldn't you do this? wchar_t buffer[32];
const auto length = GetClassNameW(GetConsoleWindow(), &buffer[0], 32);
const auto isConhost = length == 18 && memcmp(&buffer[0], L"ConsoleWindowClass", 36) == 0; With ConPTY the I would not test for |
Both questions are valid, depending on what the code is trying to determine (or why). For example, if code wants to know whether Windows Terminal shell integration support is present, that's a Windows Terminal feature, not ConPty. |
@lhecker Here are a few more examples why a console mode program may need to know specifically which terminal program is hosting them. While ConPty manages the screen buffer, individual terminal programs are responsible for rendering the screen, and they render it differently. Those differences can be crucial for console mode apps that are trying to align things or right-justify things or measure things. "What exactly would you light up only for windows terminal users and not Visual Studio or VS Code users?"
Quick example of color emoji rendering differences |
What's a shell to do if its conpty is not attached to a terminal (ssh, for example)? Since starting this thread, I have worked out most of my difficulties. If you monkey around enough with GetConsoleWindow, GetClassName, GetWindowThreadProcessId, CreateToolhelp32Snapshot (for parent PIDs), and EnumWindows, you can figure out if your in WT (and get WT's PID and a handle to its CASCADIA_HOSTING_WINDOW_CLASS window if you want them). That's all I wanted. You could probably, and similarly, figure out if you're in any terminal emulator, and which one. But what's a shell to do ... hard-code for differences among terminal emulators? That's not for me. |
A shell could require the user (and/or the terminal itself) to tell the shell about the terminal emulator it's running in. Like I don't know how (or if) terminfo/ncurses describe color emoji capabilities in Linux terminals. |
I don't know how terminfo works. Who writes it? Who reads it? And who implements what's in it? |
A good place to start reading to understand the terminfo stuff is the link I shared on the previous reply. |
Terminal.Gui maintainer here. We feel we need this. We can't move to Virtual Terminal Sequences any time soon (esp given the perf issues). In the meantime, the conhost and WT behave so differently (as have been noted above). We've gone back and forth between testing for WT_SESSION and trying to detect "If the parent process is WindowsTerminal.exe". It's just yucky and frustrating. |
@mataha You do realize that on WIndows 10+ conhost does support ANSI seqences? You just have to enable them. Here is a function written in python that does just that: import ctypes
import ctypes.wintypes
from time import sleep
# Windows API constants
INVALID_HANDLE_VALUE = -1
GENERIC_READ = 2147483648
GENERIC_WRITE = 1073741824
FILE_SHARE_READ = 1
FILE_SHARE_WRITE = 2
OPEN_EXISTING = 3
ENABLE_VT_PROCESSING = 4
# own constant for better code
FUNCTION_FAILED = 0
def enable_vt_processing():
kernel32 = ctypes.windll.kernel32
# get stdout handle
conout = ctypes.wintypes.LPCWSTR("CONOUT$\0")
stdoutH = kernel32.CreateFileW(conout, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, ctypes.c_void_p(), OPEN_EXISTING, 0,ctypes.c_void_p())
if stdoutH == INVALID_HANDLE_VALUE:
raise ctypes.WinError(kernel32.GetLastError())
# get current mode
mode = ctypes.wintypes.DWORD()
if kernel32.GetConsoleMode(stdoutH, ctypes.byref(mode)) == FUNCTION_FAILED:
raise ctypes.WinError(kernel32.GetLastError())
# set mode if needed
if mode.value & ENABLE_VT_PROCESSING == 0:
if kernel32.SetConsoleMode(stdoutH, mode.value | ENABLE_VT_PROCESSING) == FUNCTION_FAILED:
raise ctypes.WinError(kernel32.GetLastError()) And here are the links to the documentation for the Windows API functions used: The reason I use CreateFileW on CONOUT$ instead of GetStdHandle is described here |
The GNU Octave is a cross platform project built to use the default OS terminal. Currently we would love to be able to identify if that is the new Windows Terminal. There is some incompatibility between conhost and the new Windows Terminal that is causing strange artifacts and program crashes for users of the new Windows Terminal that became very apparent when win11 made it the default. Octave is primarily developed on linux and crossbuilt for windows users, and we have few windows developers working that part of the program. For now we wanted to create a script to try to identify if Octave was started in the Windows Terminal to prompt the user to switch, but so far have just settled on checking if the the registry key was set for having Windows Console Host be system default, and prompt the Windows users to switch the system default to that if it's not. Yes, it is a rather poor workaround. but it's effective. Until we have the developer resources to better troubleshoot the conhost / windows terminal incompatibility (or finish developing a separate, cross-platform compatible terminal widget to bundle with Octave), that change solves the problem for the users. But it does come with some false positives for the prompt because it gets set off when the windows setting is "let windows decide" even if that wouldn't use the new terminal, causing some other user confusion. So, it would at least be cleaner if at startup we could positively identify the Windows terminal. |
I've been using
!((BOOL) SendMessage(GetConsoleWindow(), WM_GETICON, 0, 0))
Other ideas? Thanks. - Vince
The text was updated successfully, but these errors were encountered: