TL;DR: Wer sich sicher ist, dass sein Programm entweder in der neuen Console V2 (seit Windows 10) oder im Windows Terminal (ist in der Windows 11 Installation dabei oder kann für Windows 10 nachinstalliert werden) ausgeführt wird, kann die Komfortfunktion
nutzen um Ein- und Ausgaben für den Vitual Terminal Modus und UTF-8 Strings zu konditionieren. Dabei wie folgt vorgehen:
Ansonsten ...
Warum?
Die neue Windows Console unterstützt beginnend mit Windows 10.0.10586 ANSI Escapesequenzen im Virtual Terminal Modus, beispielsweise um Text farbig auszugeben. Siehe:
Consoleanwendungen müssen die Console aber zunächst konditionieren um diese Escapesequenzen auch nutzen zu können.
Ziele dieser kleinen Lib sind:
Wie?
Zwei Dinge sind zu berücksichtigen, die völlig unabhängig voneinander existieren.
Darüber hinaus ist die
Der folgende Testcode zeigt beispielhaft, wie mit der Lib zu arbeiten ist ("win10vt.h" und "win10vt.c" sind in das Projekt eingebunden und finden sich im Download.):
Statt den C Ausgabefunktionen, könnten hier auch die C++ Ausgabestreams
Dieses Batch Script ruft die kompilierte "enable_ansi_esc.exe" unter unterschiedlichen Konditionen auf. Hier soll gezeigt werden, dass der obige C Code tatsächlich in der Lage war zu erkennen, wo VT Processing möglich ist und wo nicht:
Ausgabe:
Die Ausgabe ist wie folgt zu interpretieren:
Nicht mit dem eigentlichen Virtual Terminal Modus in Verbindung stehend, aber den Erwartungen an ein Terminal folgend, ist die Fähigkeit eines Terminals einigermaßen Unicode-kompatibel rendern zu können. Hier kommen weitere Abhängigkeiten hinzu. (Und hier sind wir auch bei einem weiteren Grund, warum das bei *nix Plattformen eher reibungslos funktioniert: UTF-8 ist quasi immer und überall Standard.)
Während Windows historisch, von UCS-2 kommend, auf UTF-16 als Zeichncodierung setzt und man mit den
Aber auch das ist nicht mehr state-of-the-art. Microsoft macht kleine Schritte hin zu UTF-8 als Standard für Text-Interfaces. Der Standardzeichensatz der Console ist allerdings noch nicht auf UTF-8 ausgerichtet, was sich aber programmatisch einstellen lässt.
Für Unicode Unterstützung stehen die Funktionen
zusammen mit den Macros
zur Verfügung.
Unicode Troubleshooting:
Zuletzt noch die zu Beginn bereits genannte Komfortfunktion
Diese versucht sowohl den VT Modus, als auch die UTF-8 Zeichenkodierung einzustellen. Nutzer dieser Funktion können aber keinen Rückschluss ziehen, ob dies erfolgreich war.
Detaillierte Beschreibungen finden sich im
void EstablishUTF8Terminal(void);
nutzen um Ein- und Ausgaben für den Vitual Terminal Modus und UTF-8 Strings zu konditionieren. Dabei wie folgt vorgehen:
win10vt.c
undwin10vt.h
in das Projekt einbinden- sicherstellen, dass der eigene Quellcode UTF-8 codiert und ohne Byte Order Mark gespeichert wird
win10vt.h
includieren- Funktion
EstablishUTF8Terminal();
zu Beginn des Programms aufrufen - die I/O Funktionen für
char
-basierte Strings verwenden, um mit UTF-8 Ein- und Ausgaben zu arbeiten.
test.c:
#include "win10vt.h"
#include <stdio.h>
int main(void) {
EstablishUTF8Terminal();
puts(ESC([42m) "Hello World!" ESC([0m) "\n"
"$ £ Ω É س Я ה ह € 한 𐍈 𤭢 \n"
"😀 👍 🙋 👀 🦋 🌹 🍀 🍞 🍺 🏠 🚗 ✈ 🌦 🌈 🎈 🎁 ⚽ 🎸 ");
}
Ansonsten ...
Warum?
Die neue Windows Console unterstützt beginnend mit Windows 10.0.10586 ANSI Escapesequenzen im Virtual Terminal Modus, beispielsweise um Text farbig auszugeben. Siehe:
Consoleanwendungen müssen die Console aber zunächst konditionieren um diese Escapesequenzen auch nutzen zu können.
Ziele dieser kleinen Lib sind:
- Auskapseln der erforderlichen Windows API aus dem Benutzercode.
- Konditionierung der Windows Console für den VT Modus, bzw. dessen Deaktivierung.
- Identifikation der Standardstreams die im VT Modus verwendbar sind, um verhindern zu können, Escapesequenzen auszugeben, die nicht gerendert werden und stattdessen in ihrer Textrepräsentation verarbeitet werden.
Wie?
Zwei Dinge sind zu berücksichtigen, die völlig unabhängig voneinander existieren.
- Das Consolefenster besitzt einen Eingabe- und einen Ausgabepuffer. Für beide kann der VT Modus im Hostprozess konfiguriert werden, sofern die Anwendung mit einem Consolefenster verknüpft ist und es sich um das neue Windows 10 V2 Consolefenster handelt (conhost.exe ohne angegliederten cscss.exe Runtime Prozess). Ob diese Konfiguration möglich oder nicht möglich ist, gibt letztlich darüber Aufschluss und ist eines der Entscheidungskriterien ob mit Escapesequenzen gearbeitet werden kann. Die
SetVTConMode
Funktion ist zu diesem Zweck implementiert. - Wenn das Consolefenster konfiguriert werden konnte, bedeutet das noch nicht, dass die Standardeingabe, Standardausgabe und Standardfehlerausgabe auch mit diesem verknüpft sind. Die entsprechenden Handles können durchaus auf eine Datei oder eine Pipe umgeleitet sein, wo Escapesequenzen nicht gerendert werden können. Der Rückgabewert der
GetStdReferCon
Funktion beinhaltet Informationen, ob das StdIn auf ConIn, und ob StdOut und StdErr auf ConOut verweisen.
Darüber hinaus ist die
TargetIsWindowsConsole
Funktion implementiert, die Aussage darüber gibt, ob an den derzeitigen Prozess überhaupt eine Windows Console angehängt ist. Bei anderen Virtual Terminal Anwendungen ist oft davon auszugehen, dass sie VT Escapesequenzen von vorn herein unterstützen.Der folgende Testcode zeigt beispielhaft, wie mit der Lib zu arbeiten ist ("win10vt.h" und "win10vt.c" sind in das Projekt eingebunden und finden sich im Download.):
enable_ansi_esc.c:
#include "win10vt.h"
#include <stdio.h>
int main(void) {
// Figure out which of the standard handles refer to the console window (rather than to a file or a pipe).
unsigned con_connected = GetStdReferCon();
// Try to enable VT processing for the handles of the attached console window, and retrieve for which it happened to work.
unsigned vt_enabled = SetVTConMode(VT_CONALL, VT_CONALL);
if ((vt_enabled & VT_CONOUT) && (con_connected & VT_STDOUT)) // VT sequences can be used for stdout.
{
fputs(ESC([42m) "Hello Narrow World!\n" ESC([0m), stdout);
fputws(ESCW([42m) L"Hello Wide World!\n" ESCW([0m), stdout);
} else // VT sequences not supported for stdout.
{
fputs("Hello Narrow World!\n", stdout);
fputws(L"Hello Wide World!\n", stdout);
}
if ((vt_enabled & VT_CONOUT) && (con_connected & VT_STDERR)) // VT sequences can be used for stderr.
fputs(ESC([41m) "Hello Wrong World!\n" ESC([0m), stderr);
else // VT sequences not supported for stderr.
fputs("Hello Wrong World!\n", stderr);
// Disable VT processing for the handles of the attached console window to be sure subsequent tests are not influenced.
SetVTConMode(VT_CONALL, VT_CONNONE);
return 0;
}
std::cout
, std::wcout
und std::cerr
oder die entsprechenden Windows API Funktionen wie WriteFile
zum Einsatz kommen, um die Effekte zu testen. Das Resultat bliebe gleich.Dieses Batch Script ruft die kompilierte "enable_ansi_esc.exe" unter unterschiedlichen Konditionen auf. Hier soll gezeigt werden, dass der obige C Code tatsächlich in der Lage war zu erkennen, wo VT Processing möglich ist und wo nicht:
test.bat:
@echo off &setlocal
echo(
echo(*** directly
enable_ansi_esc
echo(
echo(*** pipe (err merged)
enable_ansi_esc 2>&1 | find /v ""
echo(
echo(*** pipe
enable_ansi_esc | find /v ""
echo(
echo(*** for (err merged)
for /f "delims=" %%i in ('2^>^&1 enable_ansi_esc') do echo %%i
echo(
echo(*** for
for /f "delims=" %%i in ('enable_ansi_esc') do echo %%i
echo(
echo(*** redirect to con (err merged)
>con 2>&1 enable_ansi_esc
echo(
echo(*** redirect to con
>con enable_ansi_esc
echo(
pause
Ausgabe:
Die Ausgabe ist wie folgt zu interpretieren:
- Wo VT Processing möglich war, erfolgt die Ausgabe farbig (grün bzw. rot). Standardfarben weiß auf schwarz dort, wo VT Prozessing nicht möglich ist.
- Bei einer fehlerhaften Entscheidung im Programmcode würde bei den weiß auf schwarz Ausgaben der Escapecode als Text ausgegeben werden, was aber nicht der Fall ist.
Nicht mit dem eigentlichen Virtual Terminal Modus in Verbindung stehend, aber den Erwartungen an ein Terminal folgend, ist die Fähigkeit eines Terminals einigermaßen Unicode-kompatibel rendern zu können. Hier kommen weitere Abhängigkeiten hinzu. (Und hier sind wir auch bei einem weiteren Grund, warum das bei *nix Plattformen eher reibungslos funktioniert: UTF-8 ist quasi immer und überall Standard.)
Während Windows historisch, von UCS-2 kommend, auf UTF-16 als Zeichncodierung setzt und man mit den
...W()
Windows API Funktionen auch recht gut damit arbeiten kann, ist die C Runtime darauf nur bedingt ausgerichtet. Wer also wchar_t
basierte Strings mit den C Funktionen ein- und ausgeben will, bekommt Schwierigkeiten mit Zeichen die nicht im Zeichensatz der derzeitigen Console Codepage enthalten sind. Die Standard-Streams (stdin
, stdout
und stderr
) müssen entsprechend konditioniert werden.Aber auch das ist nicht mehr state-of-the-art. Microsoft macht kleine Schritte hin zu UTF-8 als Standard für Text-Interfaces. Der Standardzeichensatz der Console ist allerdings noch nicht auf UTF-8 ausgerichtet, was sich aber programmatisch einstellen lässt.
Für Unicode Unterstützung stehen die Funktionen
unsigned SetInputEncoding(unsigned id);
undunsigned SetOutputEncoding(unsigned id);
zusammen mit den Macros
VT_UTF8
und VT_UTF16
zur Verfügung.
Unicode Troubleshooting:
- Wer UTF-8 nutzt (und ich plädiere dafür, das zu tun) sollte darauf achten, es durchgängig zu verwenden. Dazu gehört auch, dass der Programmquellcode UTF-8 codiert und ohne Byte Order Mark gespeichert wird. Nur so werden nicht-ASCII Zeichen in einem Stringliteral korrekt interpretiert. (Anderenfalls müsste jedes einzelne Byte eines Multibyte-Zeichens durch einen Escapecode dargestellt werden.) Da kann ich programmatisch allerdings nicht unterstützen Das muss jeder selbst in seinem Editor / seiner IDE erledigen.
- Sollten Zeichen nicht korrekt gerendert werden, kann das am Font liegen, der solche Zeichen nicht unterstützt. Mit "Consolas" funktioniert das in der Regel recht gut. Aber im Zweifel einfach mal einen anderen Font einstellen.
- Die Windows Console kann im Moment (Stand September 2022) noch nicht alle Zeichen rendern, die der Font unterstützt. Wer also bspw. Unterstützung für die Darstellung neuerer Smileys haben möchte, kann auf das Windows Terminal zurückgreifen. Das lässt sich auf Windows 10 als App nachinstallieren. Windows 11 wird bereits damit ausgeliefert, wo es auch als Standard Terminalanwendung konfiguriert werden kann damit Consoleanwendungen im Terminal statt in der Console ausgeführt werden.
Zuletzt noch die zu Beginn bereits genannte Komfortfunktion
void EstablishUTF8Terminal(void);
Diese versucht sowohl den VT Modus, als auch die UTF-8 Zeichenkodierung einzustellen. Nutzer dieser Funktion können aber keinen Rückschluss ziehen, ob dies erfolgreich war.
Detaillierte Beschreibungen finden sich im
win10vt.h
Header im Download.