Windows AllocConsole() erlaubt das Drucken von UTF-16-codierten Zeichenfolgen nichtC++

Programme in C++. Entwicklerforum
Guest
 Windows AllocConsole() erlaubt das Drucken von UTF-16-codierten Zeichenfolgen nicht

Post by Guest »

Ich habe in den letzten Stunden mit diesem Problem zu kämpfen und ich glaube, es macht mich wahnsinnig, also dachte ich, ich frage mal hier.

Ich habe gearbeitet in einer Windows-Anwendung. Es muss ein Fenster geöffnet werden, damit es unter dem Windows-Subsystem kompiliert wird (

Code: Select all

/SUBSYSTEM:WINDOWS
-Flag in MSVC), bedeutet dies auch, dass standardmäßig kein Terminal geöffnet wird, ich aber möchte, dass eines zum Drucken von Dingen dient, während ich daran arbeite.
Die Windows-API bietet die Lösung dafür: die AllocConsole-Funktion, die, wie es in der Dokumentation heißt, „dem aufrufenden Prozess eine neue Konsole zuweist“. Nach dem Aufruf wird tatsächlich ein Konsolenfenster geöffnet, und ich kann darauf drucken, nachdem ich freopen wie folgt aufgerufen habe:

(Beachten Sie, dass freopen wird benötigt, weil wir alles, was wir in stdout schreiben, in „CONOUT$“ usw. umleiten müssen.)

Code: Select all

bool AttachCon() {
if (AllocConsole() == FALSE) {
return false;
}

FILE *tmp;
_wfreopen_s(&tmp, L"CONIN$", L"r", stdin);
_wfreopen_s(&tmp, L"CONOUT$", L"w", stdout);
_wfreopen_s(&tmp, L"CONOUT$", L"w", stderr);

return true;
}
Das Problem tritt auf, wenn versucht wird, Zeichenfolgen mit breiten Zeichen zu drucken. Standardmäßig verwendet das Windows-Terminal die Zeichenkodierung Codepage 850, was für meine Anforderungen nicht geeignet ist. Nach einer Online-Suche können Sie schnell erkennen, dass die Lösung ein obskurer Aufruf von _setmode ist, wobei der Parameter mode auf _O_U16TEXT gesetzt ist, etwa so:

Code: Select all

_setmode(_fileno(stdin), _O_U16TEXT);
_setmode(_fileno(stdout), _O_U16TEXT);
_setmode(_fileno(stderr), _O_U16TEXT);
Soweit ich weiß, sagt dies den Funktionen, die auf dem Bildschirm drucken, im Grunde, dass der Text, den sie empfangen, in dem codiert ist, was vom Benutzer angegeben wurde, und dass dies nicht der Fall ist Ändern Sie die Codepage im Terminal (

Code: Select all

GetConsoleCP
gibt immer 850 zurück). Diese Funktionen übersetzen die Zeichenfolgen dann automatisch von einer Kodierung in die andere.

Hier liegt das Problem: Der Versuch, das oben Gesagte in einer Konsolenanwendung auszuführen, funktioniert (solange die /utf -8-Flag angegeben ist, dazu später mehr), in der Desktop-Anwendung jedoch nicht.

Die Ausgabe, die ich erhalte, wenn ich versuche, L zu drucken" Test à" ist T e s t Ó :

T e s t Ó

Beachten Sie, dass à in UTF-16 0x00E0 ist und Ó auch 0xE0 in Codepage 850. Außerdem gibt es aus irgendeinem Grund seltsame Leerzeichen zwischen den einzelnen Zeichen (ich gehe davon aus, dass es sich um das zweite Byte des UTF-16-Zeichens handelt).

Das habe ich bisher auf meinem Rechner versucht und beobachtet :
  • Standardmäßig verwendet ein wchar_t-String UTF-8, dies kann durch Kompilieren des Projekts mit dem /utf-8 Flagge (was meines Wissens nur möglich ist kann durch manuelles Festlegen unter Projekteigenschaften --> C/C++ --> Befehlszeile in Visual Studio festgelegt werden. Dadurch wird die Kodierung auf UTF-16 gesetzt (kommt mir irgendwie rückwärts vor?)

    Code: Select all

    _setmode
    gibt den vorherigen Übersetzungsmodus zurück. Beim ersten Aufruf wird 0x4000 zurückgegeben, was _O_TEXT – Text (übersetzt) entspricht, und beim zweiten Mal wird 0x10000 zurückgegeben, was _O_U16TEXT – UTF16 no BOM (übersetzt) ​​entsprechen sollte ), da ich es so einstelle, aber es ist eigentlich _O_WTEXT - UTF16 (übersetzt), (die Beschreibungen stammen aus den Kommentaren neben den Definitionen in fcntl.h. Der Versuch, eine Stückliste manuell nach stdout zu schreiben führt zu nichts.
  • Ich habe dies und das versucht, aber nichts hat sich geändert.
Hat jemand eine Idee, was das sein könnte? dass das Betriebssystem einfach vergisst, einige Dinge richtig einzurichten und das Der _setmode-Aufruf wird nicht korrekt ausgeführt.

Wenn es hilft, verwende ich Windows 11 24H2 Build 26100.2605

Vielen Dank im Voraus.
BEARBEITEN
Wie gewünscht, hier ein minimal reproduzierbares Beispiel.
Um dies zu kompilieren Sie müssen ein leeres C++-Projekt in Visual Studio erstellen (ich verwende Visual Studio 2022) und Sie müssen das Subsystem auf Windows setzen, indem Sie diese Einstellung ändern:

Code: Select all

Project Properties --> Linker --> System --> SubSystem
und setzen Sie es auf Windows.

Code: Select all

#define WIN32_LEAN_AND_MEAN
#include 
#include 
#include 
#include 
#include 

static constexpr wchar_t WINDOW_CLASS[] = L"Test";
static constexpr wchar_t WINDOW_NAME[] = L"Test Window";

bool AttachCon(void) {
if (AllocConsole() == FALSE) {
return false;
}

fflush(stdin);
fflush(stdout);
fflush(stderr);

FILE *tmp;
_wfreopen_s(&tmp, L"CONIN$", L"r", stdin);
_wfreopen_s(&tmp, L"CONOUT$", L"w", stdout);
_wfreopen_s(&tmp, L"CONOUT$", L"w", stderr);

// Nothing changes when doing this
HANDLE hConIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
HANDLE hConOut = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
SetStdHandle(STD_INPUT_HANDLE, hConIn);
SetStdHandle(STD_OUTPUT_HANDLE, hConOut);
SetStdHandle(STD_ERROR_HANDLE, hConOut);

// SetConsoleMode returns FALSE so this does nothing
// DWORD console_mode;
// if (GetConsoleMode(hConOut, &console_mode)) {
//  SetConsoleMode(hConOut, console_mode | ENABLE_VIRTUAL_TERMINAL_INPUT);
// }

if (_setmode(_fileno(stdin), _O_U16TEXT) == -1) {
// Handle error
}

if (_setmode(_fileno(stdout), _O_U16TEXT) == -1) {
// Handle error
}

if (_setmode(_fileno(stderr), _O_U16TEXT) == -1) {
// Handle error
}

// Sets the output encoding to UTF-8, I need UTF-16
// SetConsoleOutputCP(65001);

// Both of these print wrong?
wchar_t str[] = L"Test à";
wprintf(L"%ls\n", str);
std::wcout

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post