Note:
-
The following applies to regular console windows on Windows (provided by
conhost.exe
), which are used by default, including when a console application is launched from a GUI application. -
By contrast, the console windows (terminals) provided by Windows Terminal as well as Visual Studio Code’s integrated terminal provide support for VT / ANSI escape sequences by default, for all console applications.
While console windows in Windows 10 do support VT (Virtual Terminal) / ANSI escape sequences in principle, support is turned OFF by default.
You have three options:
-
(a) Activate support globally by default, persistently, via the registry, as detailed in this SU answer.
- In short: In registry key
[HKEY_CURRENT_USER\Console]
, create or set theVirtualTerminalLevel
DWORD value to1
- From PowerShell, you can do this programmatically as follows:
Set-ItemProperty HKCU:\Console VirtualTerminalLevel -Type DWORD 1
- From
cmd.exe
(also works from PowerShell):
reg add HKCU\Console /v VirtualTerminalLevel /t REG_DWORD /d 1
- From PowerShell, you can do this programmatically as follows:
- Open a new console window for changes to take effect.
- See caveats below.
- In short: In registry key
-
(b) Activate support from inside your program, for that program (process) only, with a call to the
SetConsoleMode()
Windows API function.- See details below.
-
(c) Ad-hoc workaround, from PowerShell:
-
PowerShell (Core) 7+: Enclose external-program calls in
(...)
(invariably collects all output first before printing):(.\test.exe)
-
Streaming Windows PowerShell-only alternative: Pipe output from external programs to
Write-Host
.\test.exe | Out-Host
-
See details below.
-
Re (a):
The registry-based approach invariably activates VT support globally, i.e., for all console windows, irrespective of what shell / program runs in them:
-
Individual executables / shells can still deactivate support for themselves, if desired, using method (b).
-
Conversely, however, this means that the output of any program that doesn’t explicitly control VT support will be subject to interpretation of VT sequences; while this is generally desirable, hypothetically this could lead to misinterpretation of output from programs that accidentally produce output with VT-like sequences.
Note:
-
While there is a mechanism that allows console-window settings to be scoped by startup executable / window title, via subkeys of
[HKEY_CURRENT_USR\Console]
, theVirtualTerminalLevel
value seems not to be supported there. -
Even if it were, however, it wouldn’t be a robust solution, because opening a console window via a shortcut file (
*.lnk
) (e.g. from the Start Menu or Task Bar) wouldn’t respect these settings, because*.lnk
files have settings built into them; while you can modify these built-in settings via theProperties
GUI dialog, as of this writing theVirtualTerminalLevel
setting is not surfaced in that GUI.
Re (b):
Calling the SetConsoleMode()
Windows API function from inside the program (process), as shown here, is cumbersome even in C# (due to requiring P/Invoke declarations), and may not be an option:
-
for programs written in languages from which calling the Windows API is not supported.
-
if you have a preexisting executable that you cannot modify.
In that event, option (c) (from PowerShell), discussed next, may work for you.
Re (c):
PowerShell automatically activates VT (virtual terminal) support for itself when it starts (in recent releases of Windows 10 this applies to both Windows PowerShell and PowerShell (Core) 7+) – but that does not extend to external programs called from PowerShell, in either edition, as of v7.3.2.
- Separately, in v7.2+ there is the
$PSStyle.OutputRendering
preference variable, which controls whether PowerShell commands produce colored output via the formatting system, such as the colored headers ofGet-ChildItem
output. However, this setting has no effect on (direct) output from external programs.$PSStyle.OutputRendering
defaults toHost
, meaning that only formatted output that prints to the terminal (console) is colored.$PSStyle.OutputRendering = 'PlainText'
disables coloring, and$PSStyle.OutputRendering = 'Ansi'
makes it unconditional; see this answer for more information.
However, as a workaround you can relay an external program’s (stdout) output via PowerShell’s (success) output stream, in which case VT sequences are recognized:
-
As of PowerShell (Core) 7.3.2, this only works either by enclosing the call in
(...)
or by usingOut-String
, but note that all output is invariably collected first before it is printed.[1](.\test.exe)
-
In Windows PowerShell, in addition to the above, streaming the relayed output is possible too, by piping to
Write-Host
(Out-Host
,Write-Output
orOut-String -Stream
would work too).\test.exe | Write-Host
-
Note: You need these techniques only if you want to print to the console. If, by contrast, you want to capture the external program’s output (including the escape sequences), use
$capturedOutput = .\test.exe
Character-encoding caveat: Windows PowerShell by default expects output from external programs to use the OEM code page, as defined by the legacy system locale (e.g., 437
on US-English systems) and as reflected in [console]::OutputEncoding
.
.NET console programs respect that setting automatically, but for non-.NET programs (e.g., Python scripts) that use a different encoding (and produce not just pure ASCII output (in the 7-bit range)), you must (at least temporarily) specify that encoding by assigning to [console]::OutputEncoding
; e.g., for UTF-8:
[console]::OutputEncoding = [Text.Encoding]::Utf8
.
Note that this is not only necessary for the VT-sequences workaround, but generally necessary for PowerShell to interpret non-ASCII characters correctly.
PowerShell Core (v6+), unfortunately, as of v7.3.2, still defaults to the OEM code page too, but that should be considered a bug (see GitHub issue #7233), given that it otherwise defaults to UTF-8 without BOM.
[1] Using Out-String -Stream
or its built-in wrapper function, oss
, is tempting in order to achieve streaming output, but this no longer works as of PowerShell 7.3.2, possibly due to the optimization implemented in GitHub PR #16612.