I'm picking backup my HMH project after a year off
Using VS code , it complains:
a value of type "const char *" cannot be assigned to an entity of type "LPCWSTR"
on this line:
WindowClass.lpszClassName = "handmadeHeroWindowClass";
It doesn't prevent compiling.
Why am I being told this const char *
value is incorrect, when it clearly compiles?
How do I properly assign a string to WindowClass.lpszClassName
so that VS code longer sees it as an error?
LPCWSTR
is a pointer to a "wide" character string (the W in the name), which is a 16 bit per character string to encode unicode characters (utf 16 encoding).
Take a look at the remark at the end of this page https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassexa.
To make it simple there are "two version" of parts the win32 API, one that uses ANSI (~ASCII) and one that uses UNICODE. Depending on the definition of the UNICODE
preprocessor constant, some function and struct will be defined to a version or the other.
For example the WNDCLASSEX
struct is a macro that is equivalent to WNDCLASSEXA
in ANSI and WNDCLASSEXW
in unicode. In a similar way RegisterClassEx
can be RegisterClassExA
or RegisterClassExW
.
Casey will talk about that at some point in the series (near the beginning).
In your case to suppress the warning, you can use
WindowClass.lpszClassName = L"handmadeHeroWindowClass"; // or WindowClass.lpszClassName = TEXT("handmadeHeroWindowClass"); // Seed https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-text
A side note, is that LPCWSTR stands for
In full: long pointer to a constant wide string.
I would recommend against using TEXT() macro. There is zero reason nowadays to write code for supporting both A and W functions. Simply always use explicitly version you want - witch would be W variant on modern Windows. This means using WNDCLASSEXW explicitly (not WNDCLASSEX) and using functions with W suffix explicitly. And having all string literals with L
prefix.
thanks for your advice.
Prefixing with "L" causes a compile error
WindowClass.lpszClassName = L"handmadeHeroWindowClass";
.../win32_handmade.cpp(277): error C2440: '=': cannot convert from 'const wchar_t [24]' to 'LPCSTR'
.../win32_handmade.cpp(277): note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
TEXT()
seems to silence VS Code's complaint and compiles successfully. Which is odd because VS Code tells me that it expands to the same thing - a "L" prefix. I assume TEXT()
is a 'function-style cast'?
All TEXT("foo")
does is to expand it to either "foo"
or L"foo"
depending on whether you have UNICODE macro defined. Same macro affects whether macro like CreateWindowEx will be CreateWindowExA
(accepting const char* strings) or CreateWindowExW
(accepting const wchar_t* strings). Similar for types WNDCLASSEX is a macro either for WNDCLASSEXA or WNDCLASSEXW in same way.
You should not trust whatever highlighting IDE's show you. Because they often get macros wrong and cannot correctly infer correct C types. Trust & read only actual compiler errors.
Using explicitly correct types & functions with A and W suffix may prevent such IDE errors, but not guaranteed.
Thanks for your replies, and reminding me that there are both ASCII and UNICODE versions of many of these classes.
Replacing these calls
WNDCLASS WindowClass = {};
if (RegisterClass(&WindowClass)){...}
HWND Window = CreateWindow(...)
with the explicitly "wide" versions:
WNDCLASSEXW WindowClass = {};
if (RegisterClassExW(&WindowClass)){...}
HWND Window = CreateWindowExW(...)
and using the "long" prefix L"StringVariable"
seems remove the VS Code complaints & compiles correctly.
BUT: with those changes, the .exe no longer does anything I can see. CreateWindowExW() no longer returns a result.
What does "CreateWindowExW() no longer returns a result" means? This function always returns result. What value exactly it returns?
Or you mean process crashes? In such case probably arguments to this function are wrong.
the problem, I think, is that one of the 'if' evaluations for RegisterClassExW
or CreateWindowExW
is false:
if (RegisterClassExW(&WindowClass)) // this is false... { HWND Window = CreateWindowExW( 0, WindowClass.lpszClassName, L"Handmade Hero", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, Instance, 0); if (Window) // or this is false { // game loop here }
I reverted my cod back to the generic RegisterClass
and CreateWindowEx
as they are the only ones that compile to a working executable, so I can't go back and step through carefully right now.
In such cases you should use debugger. Put breakpoints and step over line by line to see how code executes and what are values of functions.
I moved on to episode 006 and Casey Addresses this 'W" vs "A" API calls a little
It looks like he is opting to do the "A" style calls for now