如何使用 /utf-8 和 UTF-8 清单处理 WM_CHAR

问题描述 投票:0回答:1

我无法弄清楚如何更新 WM_CHAR 处理程序以使用新的 /utf-8 执行模式 + manifest。如果我使用带有 unicode 字符的源字符串,我可以正常工作。 Windows 标题和控制台将按应有的方式显示它们,但现在我希望我的用户输入也能正确处理它。

我在任何地方都找不到有关它的文档,所以在某些时候我尝试手动找出它,但没有成功。

所以我尝试像这样打印数据:

case WM_CHAR:
    println("WM_CHAR: 0x{:X}[{}]", (uint32)wParam, GetUTF8CharCount((uint32)wParam));

计数函数是这样的:

static uint GetUTF8CharCount(uint32 c) {
    if (c <= 0x7F) {
        /* U+0000 ... U+007F */
        return 1;
    } else if (c < 0x07FF) {
        /* U+0080 ... U+07FF */
        return 2;
    } else if (c < 0xFFFF) {
        /* U+0800 ... U+FFFF */
        return 3;
    } else {
        /* U+10000 ... U+10FFFF */
        return 4;
    }
}

如果我在文本框中输入“ウ”,则会打印以下信息:

[Info]: WM_CHAR: 0x4581[3]
[Info]: WM_CHAR: 0x4581[3]
[Info]: WM_CHAR: 0xA6[2]

这个网站告诉我只有最后一个字节是正确的。 这对我来说毫无意义,我该如何解释这条消息?

编辑:

另外我还打印了以下信息:

CPINFOEX cpInfo;
GetCPInfoEx(CP_ACP, 0, &cpInfo);
println("ACP: {}, IsWindowUnicode: {}", GetACP(), IsWindowUnicode(hWnd));
println("CPINFOEX: {{{}, {}, {}, {}, {}}}", cpInfo.CodePage, std::string(cpInfo.CodePageName), *((uint16*)cpInfo.DefaultChar), cpInfo.MaxCharSize, (uint32)cpInfo.UnicodeDefaultChar);

结果:

[Info]: ACP: 65001, IsWindowUnicode: 0
[Info]: CPINFOEX: {65001, 65001 (UTF-8), 63, 4, 65533}

强调它确实有效(如果我直接在源代码中添加它):

编辑2:

似乎与主动语言有关。如果我将键盘布局设置为美国国际(我的默认设置),则按 Win+;输入 🎉 表情符号,我得到

F0 9F 8E 89
,但切换到日语会导致
0x4581
出现

c++ winapi utf-8
1个回答
0
投票

经过几个小时的修补,我终于找到了一种适用于所有语言的方法,同时仍然能够在启用 UTF-8 代码页 并设置 /utf-8 标志的情况下运行。这个想法是将所有内容设置为 utf-8 模式(除了 Win32 窗口)。所以我们应该使用 RegisterClass(Ex)W

CreateWindowW
。对于窗口标题等其他 API,如果我们愿意并且仍然支持 unicode,我们可以使用
A
版本。
如果设置正确

WM_CHAR

将发送UTF-16字符。它可能会以代理对的形式发送它们,您需要将它们组合成一个代码点。

case WM_CHAR: {
    if (IS_HIGH_SURROGATE(wParam)) {
        window->highSurrogate = (uint32_t)wParam; // Store the first uint16, we will need to wait for the second WM_CHAR message to get the complete pair
        break;
    }

    uint32_t codePoint;
    if (IS_SURROGATE_PAIR(window->highSurrogate, wParam)) { // Check if we have a complete pair now
        codePoint = ((window->highSurrogate - HIGH_SURROGATE_START) << 10) + ((uint32_t)wParam - LOW_SURROGATE_START) + 0x10000;
    } else { // Otherwise this is already the code point (in the lower unicode range)
        codePoint = (uint32_t)wParam;
    }

    ProcessChar(codePoint);
    break;
}

请注意,您必须将 

highSurrogate 存储在临时变量中,该变量可用于下次调用

WM_CHAR

回顾一下;

确保将文件放置在编译器可以找到的地方。如果不能,您不会收到错误。

app.manifest

内容:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
    <assemblyIdentity type="win32" name="MyAppName" version="1.0.0.0"/>
    <asmv3:application>
        <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">
            <activeCodePage>UTF-8</activeCodePage>
        </asmv3:windowsSettings>
    </asmv3:application>
</assembly>

要从代码点获取 UTF-8 字符,您可以使用以下代码:

std::vector<uint8_t> ConvertCodePointToUTF8(uint32_t code) { std::vector<uint8_t> utf8_bytes; if (code <= 0x7F) { /* U+0000 ... U+007F */ utf8_bytes.push_back(static_cast<uint8_t>(code)); } else if (code <= 0x7FF) { /* U+0080 ... U+07FF */ utf8_bytes.reserve(utf8_bytes.size() + 2); utf8_bytes.push_back(static_cast<uint8_t>(0xC0 | ((code >> 6) & 0x1F))); utf8_bytes.push_back(static_cast<uint8_t>(0x80 | (code & 0x3F))); } else if (code <= 0xFFFF) { utf8_bytes.reserve(utf8_bytes.size() + 3); /* U+0800 ... U+FFFF */ utf8_bytes.push_back(static_cast<uint8_t>(0xE0 | ((code >> 12) & 0x0F))); utf8_bytes.push_back(static_cast<uint8_t>(0x80 | ((code >> 6) & 0x3F))); utf8_bytes.push_back(static_cast<uint8_t>(0x80 | (code & 0x3F))); } else { utf8_bytes.reserve(utf8_bytes.size() + 4); utf8_bytes.push_back(static_cast<uint8_t>(0xF0 | ((code >> 18) & 0x07))); utf8_bytes.push_back(static_cast<uint8_t>(0x80 | ((code >> 12) & 0x3F))); utf8_bytes.push_back(static_cast<uint8_t>(0x80 | ((code >> 6) & 0x3F))); utf8_bytes.push_back(static_cast<uint8_t>(0x80 | (code & 0x3F))); } return utf8_bytes; } ... auto utf8Char = ConvertCodePointToUTF8(codePoint); std::cout << std::format("WM_CHAR Result: {}, CodePoint: 0x{:X}\n", std::string{ utf8Char.begin(), utf8Char.end() }, codePoint);

© www.soinside.com 2019 - 2024. All rights reserved.