18

Visual C++ 2012 RC, Win7

简体中文

项目属性 > 使用多字节字符集

当我运行这个程序时,窗口的标题显示一个字母“S”,而不是整个单词“Sample”。

#pragma comment(linker, "/SubSystem:Windows")

#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, int) {
    WNDCLASSW wc = { 0 };

    wc.style            = CS_VREDRAW | CS_HREDRAW;
    wc.hInstance        = hInstance;
    wc.hIcon            = LoadIcon(nullptr, IDI_APPLICATION);
    wc.hCursor          = LoadCursor(nullptr, IDC_ARROW);
    wc.hbrBackground    = reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
    wc.lpszClassName    = L"MyWindowClass";

    wc.lpfnWndProc = [](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
        if (uMsg - WM_DESTROY)
            return DefWindowProc(hWnd, uMsg, wParam, lParam);
        else {
            PostQuitMessage(0);
            return HRESULT();
        }
    };

    RegisterClassW(&wc);

    CreateWindowExW(0, L"MyWindowClass", L"Sample",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, SW_SHOW, CW_USEDEFAULT, 0,
        nullptr, nullptr, hInstance, nullptr);

    for (MSG msg; GetMessage(&msg, nullptr, 0, 0); DispatchMessage(&msg));
}

如果我使用 Unicode(项目属性),保持源代码不变,窗口标题显示“示例”,看起来是正确的。

如果我使用多字节,在源代码中我使用 WNDCLASS = { ..., "MyWindowClass" } 和 RegisterClassA,保持 CreateWindowExW 不变,窗口标题显示单词“Sample”,看起来正确。

如果我使用多字节,在源代码中我使用 CreateWindowExA("MyWindowClass", "Sample"),保持 WNDCLASSW 和 RegisterClassW 不变,窗口标题显示字母“S”。

是什么让它显示一个“S”,我做错了什么吗?

附加

如果我保持所有不变,即使用多字节,使用上面显示的代码,窗口标题显示字母“S”。

(如果您运行此程序并在窗口标题上看到“Sample”,而不是“S”,那么它更可能是 vc++ 2012(或 OS)的 chs 版本的特定问题)。

4

6 回答 6

32

您的代码中的问题是您使用DefWindowProc的是DefWindowProcW. 更改它将修复代码。

理想情况下,您应该将项目设置更改为使用 Unicode,而不是多字节字符集。这将简化一切,您可以使用宏,而不是像您一样CreateWindowExRegisterClassEx式使用 Unicode / ANSI 版本。

正如其他人所说,这是字符集之间的不匹配。

理想情况下,您应该在所有相互交互的 API 调用之间匹配字符集。所以如果你使用CreateWindowExW你也应该使用RegisterClassExW, DefWindowProcW, DispatchMessageW...

于 2012-08-09T13:16:56.223 回答
6

这个很不错,学到了新东西!

你需要改变

return DefWindowProc(hWnd, uMsg, wParam, lParam);  

if(IsWindowUnicode(hWnd))  
  return DefWindowProcW(hWnd, uMsg, wParam, lParam);  
else  
  return DefWindowProcA(hWnd, uMsg, wParam, lParam);

甚至更好:坚持一种字符编码。充其量只是使用RegisterClass,CreateWindowEx等等,让编译器采用正确的 Unicode 或 ANSI 函数。

于 2012-08-09T14:20:42.550 回答
2

CreateWindowExA 将字符串解释为 8 位字符。L"Sample" 的后 8 位为零,因为它的第一个字符是 0x0053 - L 表示使用宽字符。因此,该函数将其解释为 1 个字符的以空字符结尾的字符串。

于 2012-08-09T13:06:05.140 回答
1

我认为msdn 页面RegisterClass提示了此处失败的原因,在备注部分它提到如果您使用宽字符或 ansi 支持,那么它将以这种格式传递内部文本参数/消息(宽字符/ansi )。很可能这就是窗口标题所发生的事情,即使我们说的是 use CreateWindowExA,这在内部不起作用,因为 Windows SDK 已将该字符串编码为宽字符串,并且CreateWinowExA尝试将其输出为 Ansi 字符串.

简而言之,除非您有充分的理由这样做,否则不要混合 W 和 A 方法,如果您想要宽字符支持定义您的 UNICODE 宏,请让 windows 标题为您处理它。

于 2012-08-09T13:41:40.543 回答
0

在您的最后一种情况下,您的 L"Sample" 仍然是 Unicode,不是吗?您可以使用 _T() 宏,它会根据项目的 Unuicode 设置自动添加或删除 L 前缀。

正如@Pete 已经说过的那样,Unicode L"Sample" 在 ascii 中是 "S\0...",这就是为什么只打印一个符号的原因。

于 2012-08-09T13:06:03.993 回答
0

我很高兴我找到了这个。我一直在寻找答案,似乎很难用 Google 等正确找到。我确实发现针对特定程序报告的类似问题,总是归咎于“某些插件”。

这令人抓狂,因为 WndProc 的问题与调用 CreateWindowEx 或 RegisterClassEx 相去甚远!

顺便说一句,我明确使用 -W 后缀,因为我想制作一个适用于任何一种方式构建的程序的 DLL,或者克服我正在添加的程序的非 Unicode 设置。

于 2012-08-30T05:26:33.893 回答