【问题标题】:C++ : Why this window title gets truncated?C++:为什么这个窗口标题会被截断?
【发布时间】:2012-08-09 12:59:26
【问题描述】:

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(项目属性),保持源代码不变,窗口标题显示“Sample”,看起来正确。

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

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

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

追加

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

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

【问题讨论】:

  • 为什么您发布了正确运行版本的代码?不起作用的版本在诊断问题时会更有用。
  • 这看起来很奇怪。使用的 RegisterClass 版本确定它是否是 Unicode 窗口 (IsWindowUnicode())。 Windows 进行两种方式转换 Unicode ANSI,因此您应该能够混合 Unicode 和 ANSI 函数调用。但是:你为什么要这样做?为什么不只使用其中一个,最好是最后没有 A 和 W 的那些,以便 windows 头文件映射到项目属性中设置的版本(例如 RegisterClass - 没有 A 或 W,映射到 @987654324 @ 或 RegisterClassW 自动)?
  • @Joe Gauterin 如果上面发布的代码是正确的,在窗口标题中正确显示“Sample”,那么这可能是 VC++ 2012 RC chs 上的错误。上面发布的代码在我的系统上显示了一个“S”:D
  • @user1345484:您的问题指出,仅当您使用CreateWindowExA 时才会出现问题,但您发布的代码使用CreateWindowExW
  • @Joe Gauterin 抱歉误导!我将编辑帖子以使其更清晰。顺便说一句,当你运行上面显示的代码时,你的窗口标题会显示一个单词“Sample”还是一个字母“S”?

标签: c++ winapi


【解决方案1】:

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

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

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

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

【讨论】:

  • 这显然是最佳实践。但是,您在哪里找到了不能混合使用 RegisterClassW 和 CreateWindowA 的定义? IsWindowUnicode 的 MSDN 文档说您可以混合使用 Unicode 和 ANSI(窗口和消息)。不幸的是,他们没有提及 CreateWindowExA/W 和 RegisterClassA/W。
  • @WernerHenze:你说得对,这不是必须的。事实上,我错了CreateWindowExRegisterClassEx 之间的不匹配。这实际上是RegisterClassExDefWindowProc 之间的不匹配。
  • @tenfour:DefWindowProcW,没错!问题已解决,谢谢!使用 RegisterClassW/CreateWindowExW 时,必须调用 DefWindowProcW。对 Werner Henze 说:“这显然是最佳实践。但是您从哪里找到一个定义说您不能将 RegisterClassW 和 CreateWindowA 混合使用?”同意:D
  • 我在导入 WinApi 和创建窗口时遇到了与 C# 相同的问题。似乎所有处理HWND 的函数或仅传递WPARAMLPARAM 的函数必须使用相同的字符集。因为WPARAMLPARAM 可以包含导致问题出现的结构和/或字符串。
  • @tenfour 你真是个天才! DefWindowProc 是问题所在(花了我半天时间)。谁想的?顺便说一句,仅在项目级别打开 _UNICODE 开关并不总是可行的。在转换大型遗留代码库时,有时最好通过有选择地转换子部分来逐渐过渡到 Unicode。
【解决方案2】:

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

你需要改变

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

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

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

【讨论】:

  • 谢谢!在实践中,我会坚持使用这些 macor Api 和 TEXT()。至于乐趣,我想看起来很奇怪。 :D
【解决方案3】:

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

【讨论】:

  • 另外,不要使用 CreateWindowExA 或 CreateWindowExW,只需使用 CreateWindowEx 并让预处理器根据您的字符集设置确定您应该使用哪个。所有其他 Win32 函数都一样。
  • 原发帖人说,万一他调用CreateWindowExA("MyWindowClass", "Sample"),窗口标题不正确。他没有在那里写L"Sample"
【解决方案4】:

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

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

【讨论】:

    【解决方案5】:

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

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

    【讨论】:

    • 您将_T()CreateWindowEx 宏一起使用。在这里,他明确使用CreateWindowExW,所以这不相关。
    • 请再看一遍我的帖子,我说的只是作者的最后一个案例(他有问题的地方)。他在那里使用 CreateWindowExA。
    • 原发帖人说如果他调用 CreateWindowExA("MyWindowClass", "Sample") 窗口标题不正确。他没有在那里写 L"Sample"。
    【解决方案6】:

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

    这令人抓狂,因为 WndProc 的问题远不及调用 CreateWindowEx 或 RegisterClassEx!

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-30
      • 2011-07-20
      • 1970-01-01
      • 2013-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多