为什么和我的字符串相关的错误信息会彼此矛盾?
我在为一个大学课程的项目写一个C++程序,下面是我写下的代码:
const wchar_t g_szClassName[] = L"myWindowClass";
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName; //this one
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, L"Window Registration Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
L"The title of my window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, L"Window Creation Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Step 3: The Message Loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
在这段代码中,只要我使用与字符串相关的变量,就会得到相互冲突的错误信息。以 g_szClassName[] 为例,我试着把它同时做成一个 char 和一个 wchar_t 变量,在代码块中:
wc.lpszClassName = g_szClassName;
我得到了不同的错误信息。When the variable is a char variable, it gives the following error:
a value of type "const char " cannot be assigned to an entity of type "LPCWSTR" (aka "const WCHAR ")
当该变量是一个 char 变量时,给出如下错误:
"> a value of type "const char " cannot be assigned to an entity of type "LPCWSTR" (aka "const WCHAR ")"
When the variable is a wchar_t, I get the following error instead:
'=': cannot convert from 'const wchar_t [14]' to 'LPCSTR'
当该变量是一个 wchar_t,我得到的是如下错误:
"> '=': cannot convert from 'const wchar_t [14]' to 'LPCSTR'"
Going by what these error messages are saying, it seems like lpszClassName is simultaneously a LPCWSTR and LPCSTR at the same time. Is this possible in C++?
从这些错误信息所表达的意思来看,似乎 lpszClassName 同时是一个 LPCWSTR 和一个 LPCSTR。在C++里,这可能吗?
I noticed the format is different between the two, and the messages come from different sources, so I am unsure if the error is due to an issue with my environment, or something relating to my code.
我注意到两者的格式不同,错误信息来自不同来源,因此我不确定错误究竟是由于我的环境问题,还是与我的代码有关。
What can I do to solve this issue?
我该怎么做来解决这个问题?
解决方案
The only way that the statement wc.lpszClassName = g_szClassName; can produce different errors is if you are compiling it in different contexts with different compiler configurations.
导致 wc.lpszClassName = g_szClassName; 这一语句产生不同错误的唯一途径,是你在不同的上下文中以不同的编译器配置进行编译。
Most Win32 API structs and functions that deal with strings are declared to use TCHAR strings, which map to either wchar_t or char depending on whether or not UNICODE is defined during the precompile stage. Such types and functions also typically have W or A appended to their names, depending on UNICODE. For example:
大多数Win32 API处理字符串的结构体和函数都声明为使用 TCHAR 字符串,这些字符串在预编译阶段是否定义 UNICODE 决定映射为 wchar_t 还是 char。此类类型和函数的名称通常还会根据 UNICODE 在末尾附加 W 或 A。例如:
typedef struct tagWNDCLASSEXA {
...
LPCSTR lpszMenuName;
LPCSTR lpszClassName;
...
} WNDCLASSEXA, ...;
typedef struct tagWNDCLASSEXW {
...
LPCWSTR lpszMenuName;
LPCWSTR lpszClassName;
...
} WNDCLASSEXW, ...;
#ifdef UNICODE
typedef WNDCLASSEXW WNDCLASSEX;
...
#else
typedef WNDCLASSEXA WNDCLASSEX;
...
#endif // UNICODE
Which means, WNDCLASSEX wc; is actually compiled as either WNDCLASSEXA wc; or WNDCLASSEXW wc; depending on your setup.
这意味着,WNDCLASSEX wc; 实际上会根据你的设置被编译成 WNDCLASSEXA wc; 或 WNDCLASSEXW wc;。
This is explained in more detail on MSDN:
在MSDN上有更详细的解释:
Get Started with Win32 and C++ > Working with Strings
As such...
因此...
When the variable is a
charvariable, it gives the following error:a value of type "const char " cannot be assigned to an entity of type "LPCWSTR" (aka "const WCHAR ")
当变量是一个
char变量时,它会产生以下错误:类型为 "const char " 的值不能赋给类型为 "LPCWSTR"(也就是 "const WCHAR ")的实体
This happens only when you compile the translation unit (.cpp file) with UNICODE defined, where WNDCLASSEX will map to WNDCLASSEXW, which uses wchar_t strings.
这只有在你把翻译单元(.cpp 文件)在定义了 UNICODE 的情况下进行编译时才会发生,其中 WNDCLASSEX 将映射为 WNDCLASSEXW,它使用 wchar_t 字符串。
When the variable is a
wchar_t, I get the following error instead:'=': cannot convert from 'const wchar_t [14]' to 'LPCSTR'
当变量是一个
wchar_t时,我得到的是如下错误:'=': 无法从 'const wchar_t [14]' 转换为 'LPCSTR'
This happens only when you compile the translation unit without UNICODE defined, so WNDCLASSEX will map to WNDCLASSEXA, which uses char strings.
这只在未定义 UNICODE 的情况下编译翻译单元时才会发生,因此 UNICODE 将映射为 WNDCLASSEXA,它使用 char 字符串。
The same issue applies to other APIs used in your code, including DefWindowProc, RegisterClassEx, CreateWindowEx, GetMessage, MessageBox, etc.
同样的问题也同样适用于你代码中使用的其他API,包括 DefWindowProc、RegisterClassEx、CreateWindowEx、GetMessage、MessageBox 等。
Don't mix your precompiler configurations across translation units. Pick one and stick with it consistently.
不要在不同的翻译单元之间混用预处理配置。选定一种并始终如一地坚持下去。
When using strings with TCHAR-based APIs, use the TCHAR type and the TEXT() macro for literals, eg:
在使用基于 TCHAR 的API处理字符串时,使用 TCHAR 类型和 TEXT() 宏来表示字面量,例如:
const TCHAR g_szClassName[] = TEXT("myWindowClass");
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName; //this one
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, TEXT("Window Registration Failed!"), TEXT("Error!"),
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
TEXT("The title of my window"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, TEXT("Window Creation Failed!"), TEXT("Error!"),
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Step 3: The Message Loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
Otherwise, simply don't rely on TCHAR-based APIs at all, use A and W APIs explicitly as needed, eg:
否则,完全不要依赖基于 TCHAR 的API,按需显式使用 A 和 W API,例如:
const wchar_t g_szClassName[] = L"myWindowClass";
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEXW wc;
HWND hwnd;
MSG Msg;
//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName; //this one
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassExW(&wc))
{
MessageBoxW(NULL, L"Window Registration Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Step 2: Creating the Window
hwnd = CreateWindowExW(
WS_EX_CLIENTEDGE,
g_szClassName,
L"The title of my window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBoxW(NULL, L"Window Creation Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Step 3: The Message Loop
while(GetMessageW(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessageW(&Msg);
}
return Msg.wParam;
}