консольное приложение может использовать разные способы вывода.
- для дескриптора консоли мы можем использовать
WriteConsoleW
для вывода уже в UNICODE
.
- если мы хотим использовать
WriteConsoleA
a> или WriteFile
для дескриптора консоли необходимо сначала преобразовать UNICODE
текст в несколько байтов с помощью WideCharToMultiByte
с CodePage :=
GetConsoleOutputCP()
- если у нас изначально не
UNICODE
текст для вывода (скажем, UTF-8
или Ansi
), необходимо сначала преобразовать его в UNICODE
с помощью MultiByteToWideChar
(с CP_UTF8
или CP_ACP
) и потом уже снова конвертировать в многобайтный WideCharToMultiByte(GetConsoleOutputCP(), ..)
обычный (по умолчанию) GetConsoleOutputCP()
< /a> возвращает то же значение, что и GetOEMCP()
, так что тот же эффект будет в MultiByteToWideChar
и WideCharToMultiByte
как CP_OEMCP
(это постоянное значение преобразуется в GetOEMCP()
)
когда выходной дескриптор перенаправляется в файл, нужно использовать только WriteFile
. однако приложение может записывать данные в файл в любом формате: UNICODE
, Ansi
(CP_ACP
), UTF-8
(CP_UTF8
) и т. д. какой формат будет использоваться - очень зависит от конкретного приложения. вы не можете полностью контролировать это. обычно вы получите многобайтовый вывод в кодировке CP_OEMCP
. затем вам нужно решить, как его обработать - быстрее всего вам нужно будет сначала преобразовать его в UNICODE
и использовать unicode
форму. если вам нужно Ansi
- вам нужно будет сделать еще одно преобразование.
скажем, если вы попытаетесь использовать вывод канала в кодировке CP_OEMCP
с OutputDebugStringA
- вы получили вывод ошибки (нечитаемый) для неанглоязычного текста. но после 2 преобразований CP_OEMCP
-> UNICODE
-> CP_ACP
вы можете исправить отображаемый текст с помощью OutputDebugStringA
, но потому что OutputDebugStringW
существуют - здесь достаточно только UNICODE
преобразовать
также в некоторых приложениях есть специальные опции для контроля вывода в формат файла. скажем, ipconfig.exe
ищет "OutputEncoding"
переменную среды и зависит от ее строкового значения ("Unicode"
, "Ansi"
, "UTF-8"
) для получения другого вывода. по умолчанию (если эта переменная среды не существует или ее значение неизвестно) CP_OEMCP
используется
пример процедуры чтения канала. предположим, что входные данные в кодировке CP_OEMCP
:
void OnRead(PVOID buf, ULONG cbTransferred)
{
if (cbTransferred)
{
if (int len = MultiByteToWideChar(CP_OEMCP, 0, (PSTR)buf, cbTransferred, 0, 0))
{
PWSTR pwz = (PWSTR)alloca((1 + len) * sizeof(WCHAR));
if (len = MultiByteToWideChar(CP_OEMCP, 0, (PSTR)buf, cbTransferred, pwz, len))
{
if (g_bUseAnsi)
{
if (cbTransferred = WideCharToMultiByte(CP_ACP, 0, pwz, len, 0, 0, 0, 0))
{
PSTR psz = (PSTR)alloca(cbTransferred + 1);
if (cbTransferred = WideCharToMultiByte(CP_ACP, 0, pwz, len, psz, cbTransferred, 0, 0))
{
DoPrint(psz, cbTransferred, OutputDebugStringA);
}
}
}
else
{
DoPrint(pwz, len, OutputDebugStringW);
}
}
}
}
}
// debugger can incomplete print too big buffer, so split it on small chunks
template<typename T> void DoPrint(T* p, ULONG len, void (WINAPI* fnOutput)(const T*))
{
ULONG cb;
T* q = p;
do
{
cb = min(len, 256);
q = p + cb;
T c = *q;
*q = 0;
fnOutput(p);
*q = c;
p = q;
} while (len -= cb);
}
о вашем конкретном случае - ipconfig.exe
используется WriteConsoleW
для вывода на консоль. в результате он не зависит от текущей локали системы и может корректно отображать многоязычный текст. но другие инструменты, такие как route.exe
, использовали WriteFile
для вывода (как на консоль, так и в файл) и преобразовать перед этим UNICODE
текст в многобайтный по WideCharToMultiByte(CP_OEMCP,..)
- в результате здесь будут проблемы, если попытаться отобразить символы, которых нет в кодовой странице CP_OEMCP
(текущая локаль системы) . если у вас CP437
- еврейские и русские символы будут потеряны, если использовать UNICODE
-> CP_OEMCP
, нужен только прямой вывод с юникодом на консоль и в файл. возможно ли это - зависит от конкретного приложения. скажем, route.exe
это невозможно. для ipconfig.exe
это возможно, потому что он всегда записывает в консоль в формате unicode и может записывать в файл также в unicode
или utf-8
, если вы установите "OutputEncoding"
в "Unicode"
или "UTF-8"
person
RbMm
schedule
03.01.2017
MultiByteToWideChar(CP_OEMCP,
- символы не теряются - person RbMm   schedule 03.01.2017CP_OEMCP can't encode all characters
- ты в этом уверен? я думаю, ты ошибаешься здесь - person RbMm   schedule 03.01.2017MultiByteToWideChar
не волшебным образом восстановит персонажей, которых нет в этом списке. - person roeland   schedule 04.01.2017MultiByteToWideChar(CP_OEMCP
- ? какой код вы используете - опять же, ничего не потеряно в многобайтовых преобразованиях - person RbMm   schedule 04.01.2017ipconfig.exe
использовалWriteConsoleW
для вывода на консоль - в результате всегда правильный вывод на любых языках и не зависит от текущих кодовых страниц. если приложение использует функцииA
или записывает в файл как многобайтовые, возникнет проблема, если попытаться напечатать символы, которые не существуют при использовании кодовой страницы - person RbMm   schedule 04.01.2017"αβ"
, который затем сокращается до набора символов OEM (вероятно, что-то вроде"ab"
) и только потом передается вашей программе. - person roeland   schedule 04.01.2017CP437
- с этимWideCharToMultiByte(CP_OEMCP)
действительно потеряли данные для иврита и русских символов.ipconfig.exe
однако используйте функциюUNICODE
для записи в консоль - в результате текст отображается правильно. - person RbMm   schedule 04.01.2017en
- поэтому у нас не 256, а 256*256 - person RbMm   schedule 04.01.2017CP_ACP
иCP_OEM
на самом деле многобайтовые. Возможно, но редко, а если CP_OEM — это обычный CP437, то он однобайтный. - person MSalters   schedule 04.01.2017CP_ACP
иCP_OEM
переводят символы Unicode на выбранную страницу. он использует преобразование один к одному символу. если скажем, мы используемHebrew
страницу - мы можем перевести (без потери данных) символы иврита и английского языка, но не русский или другой язык - person RbMm   schedule 04.01.2017