Windows AllocConsole() не позволяет печатать строки в кодировке UTF-16C++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 Windows AllocConsole() не позволяет печатать строки в кодировке UTF-16

Сообщение Anonymous »

Последние несколько часов я боролся с этой проблемой и думаю, что она сводит меня с ума, поэтому решил спросить здесь.

Я работал в приложении Windows. Ему необходимо открыть окно, чтобы оно было скомпилировано в подсистеме Windows (

Код: Выделить всё

/SUBSYSTEM:WINDOWS
в MSVC), это также означает, что он не открывает терминал по умолчанию, но я хочу, чтобы он печатал данные, пока я над ним работаю.
Windows API предоставляет решение для этого: функция AllocConsole, которая, как сказано в документации, "выделяет новую консоль для вызывающего процесса". После его вызова действительно открывается окно консоли, и я могу вывести в него текст после вызова freopen следующим образом:

(Обратите внимание, что freopen > необходим, потому что нам нужно перенаправить все, что мы пишем в стандартный вывод, на «CONOUT$» и т. д.)

Код: Выделить всё

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;
}
Проблема возникает при попытке напечатать строки расширенных символов. По умолчанию терминал Windows использует кодировку символов кодовой страницы 850, что не подходит для того, что мне нужно. После поиска в Интернете вы быстро увидите, что решение представляет собой неясный вызов _setmode с параметром mode, установленным в _O_U16TEXT, например:

Код: Выделить всё

_setmode(_fileno(stdin), _O_U16TEXT);
_setmode(_fileno(stdout), _O_U16TEXT);
_setmode(_fileno(stderr), _O_U16TEXT);
Насколько я понимаю, это в основном сообщает функциям, которые выводят на экран, что текст, который они получат, закодирован так, как указано пользователем, и что на самом деле это не так. измените кодовую страницу в терминале (

Код: Выделить всё

GetConsoleCP
всегда возвращает 850). Затем эти функции автоматически переводят строки из одной кодировки в другую.

Вот в чем проблема: попытка сделать вышеописанное в консольном приложении работает (пока /utf указан флаг -8, подробнее об этом позже), но его нет в приложении для ПК.

Вывод, который я получаю при попытке напечатать L" Тест à" — это T e s t Ó :

T e s t Ó

Обратите внимание, что à — это 0x00E0 в UTF-16, а Ó — это также 0xE0 на кодовой странице 850. Кроме того, по какой-то причине между каждым символом есть странные пробелы (я предполагаю, что это второй байт символа UTF-16).

Это то, что я пробовал и наблюдал на своей машине. :
  • По умолчанию строка wchar_t использует UTF-8, это можно изменить, скомпилировав проект с использованием /utf-8 флаг (который, насколько я могу судить, может быть только устанавливается вручную в Свойствах проекта --> C/C++ --> Командная строка в Visual Studio), при этом устанавливается кодировка UTF-16 (мне кажется, что это наоборот?)
  • Код: Выделить всё

    _setmode
    возвращает предыдущий режим перевода. При первом вызове он возвращает 0x4000, что соответствует _O_TEXT - тексту (переведенному), а во второй раз возвращает 0x10000, что должно соответствовать _O_U16TEXT - UTF16 no BOM (переведенному). ), так как я его устанавливаю, но на самом деле это _O_WTEXT - UTF16 (переведено), (описания взяты из комментариев рядом с определениями в fcntl.h. Попытка вручную записать спецификацию в стандартный вывод ничего не дает.
  • Я пробовал то и это, но ничего не изменилось.
У кого-нибудь есть идеи, что это может быть? что операционная система просто забывает некоторые настройки и Вызов _setmode выполняется неправильно.

Кроме того, если это поможет, я использую Windows 11 24H2, сборка 26100.2605

Заранее спасибо.
РЕДАКТИРОВАТЬ
В соответствии с просьбой, вот минимальный воспроизводимый пример.
Компилировать для этого вам нужно создать пустой проект C++ в Visual Studio (я использую Visual Studio 2022) и установить в качестве подсистемы значение Windows, изменив этот параметр:

Код: Выделить всё

Project Properties --> Linker --> System --> SubSystem
и установите Windows.

Код: Выделить всё

#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 

Подробнее здесь: [url]https://stackoverflow.com/questions/79351425/windowss-allocconsole-doesnt-allow-printing-utf-16-encoded-strings[/url]
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»