Что точно устанавливает параметр charset CreateFont?

Кодовая страница в моей Windows установлена ​​на ANSI (Latin1, Windows-1252).
Я создаю шрифт с помощью CreateFont и передаю RUSSIAN_CHARSET в fdwCharSet.

Вот что я испытываю:

  • Элементы управления Windows (например, Static), использующие этот шрифт, игнорируют набор символов шрифта: строка, переданная в SetWindowTextA, отображается латинскими символами.
  • После выбора этого шрифта на контроллере домена текстовые функции GDI (Ext)TextOutA и DrawTextA используют набор символов шрифта. Переданные им строки отображаются кириллическими буквами.

Почему? Когда параметр charset шрифта учитывается, а когда игнорируется? Могу ли я заставить элементы управления Windows использовать набор символов шрифта?


person Gabor Herman    schedule 17.06.2011    source источник
comment
Да, используйте SetWindowTextW(). Русский текст не может быть правильно преобразован в строку ansi с помощью SetWindowsTextA(), если кодовая страница 1252. В нем нет кириллических символов.   -  person Hans Passant    schedule 18.06.2011
comment
Я знаю, что использование широкого символа - правильный способ сделать это, но в моем случае это сложно сделать (очень большая и довольно старая система). Интересно, почему настройка кодировки шрифта работает по-разному для текстовых функций GDI и для элементов управления, нарисованных самой Windows.   -  person Gabor Herman    schedule 18.06.2011
comment
Похоже, я не должен использовать API xxxA. Юникод - это хорошо.   -  person bronze man    schedule 23.10.2017


Ответы (4)


Вам нужно будет преобразовать текст в Unicode и вызвать SetWindowTextW() вместо SetWindowTextA().

Убедитесь, что класс окна зарегистрирован с помощью RegisterClassW(), а не RegisterClassA(). Это то, что действительно определяет, является ли окно юникодом. Вы можете использовать IsWindowUnicode(), чтобы убедиться, что окно действительно имеет юникод.

Убедитесь, что вы передаете необработанные сообщения в DefWindowProcW(), а не в DefWindowProcA().

Или, если окно является диалоговым, просто убедитесь, что оно создано с помощью CreateDialogW() или DialogBoxParamW().

person Greg Wittmeyer    schedule 31.01.2012

›Можно ли заставить элементы управления Windows использовать набор символов шрифта?

AFAIK нет, вы не можете.

SetWindowTextA просто преобразует аргумент в Unicode, а затем вызывает SetWindowTextW: и ядро ​​Windows, и оболочка, и GDI имеют Unicode.

Чтобы преобразовать аргумент в Unicode, SetWindowTextA использует параметр из региональных параметров Windows («Язык для программ, не поддерживающих Unicode»).

person Soonts    schedule 21.06.2011
comment
Это не так просто. SetWindowTextA преобразуется в Unicode, только если окно было создано в режиме Unicode, а не в режиме ANSI. Для окон ANSI преобразование в Unicode происходит в момент WM_PAINT, после чего информация теряется. - person Adrian McCarthy; 22.06.2011
comment
Я тоже допускаю, что видел там такую ​​последовательность вызовов: ExtTextout -> GetCodePage -> GDIGetEntry -> NtGdiGetCharSet -> win32u.NtGdiPolyPatBlt - person Serg; 04.07.2019

Вот что происходит:

  1. Вы вызываете SetWindowText с чем-то вроде "\xC4\xEE\xE1\xF0\xEE\xE5 xF3\xF2\xF0\xEE".
  2. Вы скомпилированы как приложение ANSI, а не Unicode, поэтому это соответствует вызову SetWindowTextA.
  3. SetWindowTextA видит, что окно было создано в режиме ANSI, поэтому устанавливает строку напрямую. (Если бы это было окно в формате Unicode, то оно преобразовывало бы входную строку ANSI в Unicode и передало ее в SetWindowTextW.)
  4. Окно ANSI преобразует строку ANSI в Unicode, чтобы ее можно было отобразить. Но он не знает, что строка находится на другой кодовой странице, чем кодовая страница по умолчанию для системы. Именно это преобразование меняет все обратно на латинские символы. Предполагается, что входная строка находится на кодовой странице процесса по умолчанию (в вашем случае Windows 1252). Итак, теперь у вас есть куча латинских символов с диакритическими знаками вместо цепочки кириллических.
  5. Элемент управления пытается отобразить эту строку Unicode, используя что-то вроде DrawTextW или TextOutW.
  6. Нижняя часть системы говорит: «Вот дерьмо, эта строка состоит из набора латинских символов, но пользователь выбрал кириллический шрифт». Чтобы решить эту проблему, он использует привязку шрифтов (или резервный шрифт, я путаю эти термины), чтобы эффективно выбрать шрифт, совместимый с 1252.
  7. Вы видите латинскую абракадабру вместо собственно русской.

Я пытался придумать минимальный способ сделать то, что вам нужно, но мне это не удалось. Моей первой идеей было сделать преобразование самостоятельно и вызвать SetWindowTextW напрямую:

void SetWindowTextRussian(HWND hwnd, char *pszCyrillic) {
    const int cchCyrillic = ::lstrlen(pszCyrillic);
    const int cchUnicode = 4 * cchCyrillic;  // worst case
    WCHAR *pszUnicode = new WCHAR[cchUnicode];

    // See: http://msdn.microsoft.com/en-us/library/dd317756(v=vs.85).aspx
    const UINT CP_CYRILLIC = 1251;
    if (::MultiByteToWideChar(CP_CYRILLIC, 0, pszCyrillic, -1,
                              pszUnicode, cchUnicode) > 0) {
        ::SetWindowTextW(hwnd, pszUnicode);
    }

    delete [] pszUnicode;
}

Но это не работает. Я подозреваю, что, поскольку окно было создано как окно ANSI, строка Unicode преобразуется обратно в ANSI (предполагая, что снова неправильная кодовая страница), и тогда вы получаете вопросительные знаки вместо латинской чепухи.

Я думаю, вам придется преобразовать приложение в Unicode или работать только с кодовой страницей по умолчанию, установленной на 1251.

Обновление: если вы управляете созданием окна (например, вы вызываете CreateWindow напрямую, а не создаете экземпляры элементов управления в диалоговом окне), то вы, вероятно, можете заставить вышеописанное работать, вызвав CreateWindowW напрямую и создав Окно Unicode для важных элементов управления.

person Adrian McCarthy    schedule 21.06.2011
comment
Спасибо за ваши ответы. Я попробовал решение CreateWindowW, и оно не помогает. Вероятно, преобразование в юникод в SetWindowTextA работает так же, как и в WM_PAINT: оно использует текущую общесистемную кодовую страницу. Однако ваш SetWindowTextRussian у меня работает нормально. Решение будет чем-то похожим - тем не менее, я не вижу причины различного поведения DrawTextA и SetWindowTextA. - person Gabor Herman; 24.06.2011
comment
ОБНОВЛЕНИЕ: Хорошо, теперь я вижу, что вы предлагаете мне одновременно использовать CreateWindowW и SetWindowTextRussian. На самом деле, это действительно то, что работает нормально. - person Gabor Herman; 24.06.2011
comment
@Gabor Herman: Я рад, что у тебя все получилось. Извините, если я недостаточно ясно выразился, предлагая использовать вместе CreateWindowW и SetWindowTextRussian. - person Adrian McCarthy; 24.06.2011

Попробуйте подключить gdi32full.dll GetCodePage, чтобы выбрать нужную кодовую страницу. CP_UTF8 например. Он имеет один параметр-указатель, возвращает одиночное DWORD (кодовую страницу) и соглашение о вызовах stdcall.

person Serg    schedule 05.07.2019