CreateFont的charset参数究竟设置了什么?

时间:2011-06-17 14:44:24

标签: winapi internationalization fonts character-encoding gdi

我的Windows上的代码页设置为ANSI(Latin1,Windows-1252) 我用CreateFont创建一个字体,并在fdwCharSet

中传递RUSSIAN_CHARSET

这就是我的体验:

  • 使用此字体的Windows控件(例如静态)忽略字体的字符集:传递给SetWindowTextA的字符串显示为拉丁字符
  • 在DC上选择此字体后,GDI文本函数(Ext)TextOutA和DrawTextA使用字体的字符集。传递给他们的字符串用西里尔字母显示。

为什么呢?何时考虑字体的charset参数以及何时忽略它?我可以强制Windows控件使用字体的字符集吗?

4 个答案:

答案 0 :(得分:1)

您必须将文本转换为unicode并调用SetWindowTextW()而不是SetWindowTextA()。

确保窗口的类已注册RegisterClassW()而不是RegisterClassA()。这是真正决定窗口是否为unicode的原因。您可以使用IsWindowUnicode()来验证窗口实际上是unicode。

确保将未处理的邮件传递给DefWindowProcW()而不是DefWindowProcA()。

或者,如果窗口是对话框,只需确保使用CreateDialogW()或DialogBoxParamW()创建它。

答案 1 :(得分:0)

>我可以强制Windows控件使用字体的字符集吗?

AFAIK不,你不能。

SetWindowTextA只是将参数转换为Unicode,然后调用SetWindowTextW:windows kernel,shell和GDI都是unicode。

要将参数转换为Unicode,SetWindowTextA使用Window的区域选项(“非Unicode程序的语言”)中的设置。

答案 2 :(得分:0)

以下是发生的事情:

  1. 您使用"\xC4\xEE\xE1\xF0\xEE\xE5 xF3\xF2\xF0\xEE"
  2. 之类的方式调用SetWindowText
  3. 您被编译为ANSI应用程序而不是Unicode,因此映射到对SetWindowTextA的调用。
  4. SetWindowTextA看到窗口是以ANSI模式创建的,因此它直接设置字符串。 (如果它是一个Unicode窗口,那么它会将ANSI输入字符串转换为Unicode并将其传递给SetWindowTextW。)
  5. ANSI窗口将ANSI字符串转换为Unicode,以便它可以显示它。但它不知道字符串与系统的默认代码页位于不同的代码页中。正是这种转换将一切都改回拉丁字符。它假定输入字符串位于进程的默认代码页(在您的情况下为Windows 1252)。所以现在你有一堆重音拉丁字符而不是一串西里尔字符。
  6. 控件尝试使用DrawTextW或TextOutW等显示此Unicode字符串。
  7. 系统的下层部分说:“哦,废话,这个字符串是一堆拉丁字符,但用户选择了西里尔字体。”为了解决这个问题,它使用字体链接(或字体后备,我让这些术语混淆)来有效地选择与1252兼容的字体。
  8. 你看到拉丁语gobbledygook而不是正确的俄语。
  9. 我尝试用最小的方法来做你需要的,但我失败了。我的第一个想法是自己进行转换并直接调用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窗口。

答案 3 :(得分:0)

考虑挂接gdi32full.dll GetCodePage以选择所需的代码页。例如CP_UTF8。它具有单个指针参数,返回单个DWORD(代码页)和stdcall调用约定。