CreateFont的charset参数到底设置了什么?

发布于 2024-11-16 02:25:43 字数 356 浏览 3 评论 0原文

我的 Windows 上的代码页设置为 ANSI(Latin1、Windows-1252)。
我使用 CreateFont 创建字体并在 fdwCharSet 中传递 RUSSIAN_CHARSET

这就是我的经历:

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

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

The code page on my Windows is set to ANSI (Latin1, Windows-1252).
I create a font with CreateFont and pass RUSSIAN_CHARSET in fdwCharSet

This is what I experience:

  • Windows controls (like a Static for example) using this font ignore the font's character set: the string passed to SetWindowTextA is displayed with Latin characters
  • After selecting this font on the DC, GDI text functions (Ext)TextOutA and DrawTextA use the character set of the font. Strings passed to them are displayed with cyrillic letters.

Why? When is the charset parameter of the font taken into account and when is it ignored? Can I force windows controls to use the font's character set?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

つ可否回来 2024-11-23 02:25:43

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

确保窗口的类是通过 RegisterClassW() 而不是 RegisterClassA() 注册的。这才是真正决定窗口是否为 Unicode 的因素。您可以使用 IsWindowUnicode() 来验证窗口是否确实是 Unicode。

确保将未处理的消息传递给 DefWindowProcW() 而不是 DefWindowProcA()

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

You will have to convert the text to Unicode and call SetWindowTextW() instead of SetWindowTextA().

Make sure the window's class is registered with RegisterClassW() and not RegisterClassA(). This is what really determines if a window is Unicode. You can use IsWindowUnicode() to verify that the window is really Unicode.

Make sure you pass unhandled messages to DefWindowProcW() and not DefWindowProcA().

Or, if the window is a dialog, just make sure it is created with CreateDialogW() or DialogBoxParamW().

凉风有信 2024-11-23 02:25:43

正在发生的事情是这样的:

  1. 您使用诸如 "\xC4\xEE\xE1\xF0\xEE\xE5 xF3\xF2\xF0\xEE" 之类的内容调用 SetWindowText。
  2. 您被编译为 ANSI 应用程序而不是 Unicode,因此映射到对 SetWindowTextA 的调用。
  3. SetWindowTextA看到窗口是在ANSI模式下创建的,所以它直接设置字符串。 (如果它是 Unicode 窗口,那么它将把 ANSI 输入字符串转换为 Unicode 并将其传递到 SetWindowTextW。)
  4. ANSI 窗口将 ANSI 字符串转换为 Unicode,以便可以显示它。但它不知道该字符串位于与系统默认代码页不同的代码页中。正是这种转换将所有内容更改回拉丁字符。它假设输入字符串位于进程的默认代码页中(在您的情况下为 Windows 1252)。所以现在你有一堆带重音的拉丁字符而不是一串西里尔字符。
  5. 该控件尝试使用 DrawTextW 或 TextOutW 等方式显示此 Unicode 字符串。
  6. 系统的下层部分会说:“糟糕,这个字符串是一堆拉丁字符,但用户选择了西里尔字体。”为了解决这个问题,它使用字体链接(或字体回退,我把这些术语搞混了)来有效地选择与 1252 兼容的字体。
  7. 您会看到拉丁语 gobbledygook 而不是正确的俄语。

我尝试想出一个最小的方法来完成你需要的事情,但我失败了。我的第一个想法是自己进行转换并直接调用 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 窗口来完成上述工作。

Here is what is happening:

  1. You call SetWindowText with something like "\xC4\xEE\xE1\xF0\xEE\xE5 xF3\xF2\xF0\xEE".
  2. You're compiled as an ANSI application rather than Unicode, so that maps to a call to SetWindowTextA.
  3. SetWindowTextA sees that the window was created in ANSI mode, so it sets the string directly. (If it had been a Unicode window, then it would converts the ANSI input string to Unicode and passes it onto SetWindowTextW.)
  4. The ANSI window converts the ANSI string to Unicode so that it can display it. But it doesn't know the string is in a different code page than the default one for the system. It's this conversion that's changing everything back to Latin characters. It assumes that the input string is in the process's default code page (Windows 1252 in your case). So now you have a bunch of accented Latin characters instead of a string of Cyrillic ones.
  5. The control tries to display this Unicode string using something like DrawTextW or TextOutW.
  6. The lower level part of the system says, "Oh crap, this string is a bunch of Latin characters, but the user has chosen a Cyrillic font." To solve the problem, it uses font linking (or font fallback, I get those terms confused) to effectively select a font compatible with 1252.
  7. You see Latin gobbledygook instead of proper Russian.

I tried coming up with a minimal way to do what you need, but I failed. My first idea was to do the conversion yourself and call SetWindowTextW directly:

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;
}

But this doesn't work. I suspect that since the window was created as an ANSI window, the Unicode string is converted back to ANSI (assuming the wrong code page again), and then you get question marks instead of Latin nonsense.

I think you're going to have to convert to a Unicode app, or only run with the default code page set to 1251.

Update: If you control the creation of the window (e.g., you call CreateWindow directly rather than having a dialog box instantiate the controls), then you can probably make the above work by calling CreateWindowW directly and creating a Unicode window for the controls that matter.

宣告ˉ结束 2024-11-23 02:25:43

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

据我所知不,你不能。

SetWindowTextA 只是将参数转换为 Unicode,然后调用 SetWindowTextW:Windows 内核、shell 和 GDI 都是 unicode。

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

>Can I force windows controls to use the font's character set?

AFAIK no, you can't.

SetWindowTextA just converts the argument to Unicode, then calls SetWindowTextW: both windows kernel, shell, and GDI are unicode.

To convert the argument to Unicode, SetWindowTextA uses the setting from Window's regional options ("Language for non-Unicode programs").

逆光飞翔i 2024-11-23 02:25:43

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

Consider hooking gdi32full.dll GetCodePage to select the code page that you need. CP_UTF8 for example. It has single pointer param, returns single DWORD (the code page) and stdcall calling convention.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文