WinAPI疑难:如何正确传递LPWCSTR类型的MSFTEDIT_CLASS给CreateWindowExA
这个问题的核心是宽字符串到ANSI字符串的强制转换会破坏原字符串的结构,导致窗口类名识别失败,咱们一步步来解决:
首先说为什么强制转换不行:MSFTEDIT_CLASS是宽字符串(L"RICHEDIT50W"),每个字符占2字节。当你用(LPCSTR)MSFTEDIT_CLASS强制转换时,程序会把宽字符串的内存当成ANSI字符串来读——宽字符'R'在内存里是0x52 0x00,ANSI读取到第二个字节0x00就会认为字符串结束,实际传给CreateWindowExA的类名变成了单个字符"R",系统找不到这个类,自然返回NULL。而你直接传"RICHEDIT50W"是ANSI字符串,内存布局是连续的ASCII字节,所以能正确识别。
接下来给你几个可行的解决方案,按推荐优先级排序:
1. 改用Unicode版的CreateWindowExW(最推荐)
Windows现在优先推荐Unicode编程,直接使用宽字符版的窗口创建函数,就能直接用MSFTEDIT_CLASS这个宏,不需要任何转换:
HWND hRichEdit = CreateWindowExW( WS_EX_CLIENTEDGE, MSFTEDIT_CLASS, // 直接传入宽字符串宏,完美匹配 L"", WS_CHILD | WS_VISIBLE | ES_MULTILINE | WS_VSCROLL, 10, 10, 300, 200, hParentWnd, (HMENU)IDC_RICHEDIT, GetModuleHandle(NULL), NULL );
如果你的项目默认是Unicode编码(现在绝大多数Windows项目都是),甚至可以直接写CreateWindowEx,它会自动展开成CreateWindowExW,和上面的代码效果一致。
2. 把宽字符串转换为ANSI字符串(适合必须用ANSI版的场景)
如果因为某些原因必须使用CreateWindowExA,可以用WideCharToMultiByte函数把MSFTEDIT_CLASS的宽字符串转换成ANSI格式,再传入函数:
// 先计算转换所需的缓冲区大小 int bufSize = WideCharToMultiByte(CP_ACP, 0, MSFTEDIT_CLASS, -1, NULL, 0, NULL, NULL); if (bufSize == 0) { // 处理转换错误,比如用GetLastError()查看详情 return NULL; } // 分配内存存储转换后的ANSI字符串 char* szAnsiClass = new char[bufSize]; if (!szAnsiClass) { // 内存分配失败 return NULL; } // 执行转换 WideCharToMultiByte(CP_ACP, 0, MSFTEDIT_CLASS, -1, szAnsiClass, bufSize, NULL, NULL); // 创建富编辑窗口 HWND hRichEdit = CreateWindowExA( WS_EX_CLIENTEDGE, szAnsiClass, "", WS_CHILD | WS_VISIBLE | ES_MULTILINE | WS_VSCROLL, 10, 10, 300, 200, hParentWnd, (HMENU)IDC_RICHEDIT, GetModuleHandle(NULL), NULL ); // 记得释放内存 delete[] szAnsiClass;
这里用CP_ACP表示使用系统默认的ANSI代码页,如果你需要更通用的编码(比如UTF-8),可以换成CP_UTF8,不过窗口类名一般都是ASCII兼容的,用CP_ACP足够。
3. 定义ANSI版的宏(不推荐,冗余)
你也可以自己定义一个ANSI版的宏,比如:
#define MSFTEDIT_CLASS_A "RICHEDIT50W"
然后传入CreateWindowExA,但这本质上还是复制了头文件里的字符串,违背了你不想复制的初衷,所以不推荐这种方式。
总的来说,优先选择第一种方案,既简洁又符合Windows的现代编程规范,还能避免字符串转换带来的潜在问题。
内容的提问来源于stack exchange,提问作者Irbis

