Day 005: cannot convert from 'const wchar_t [24]' to 'LPCSTR'

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

  • LP long pointer (not sure about the long part, but it's a pointer);
  • C for constant;
  • W for wide;
  • STR for string.

In full: long pointer to a constant wide string.


Edited by Simon Anciaux on

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'?

image.png


Edited by logan bender on
Replying to mrmixer (#27081)

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.


Replying to userNameExists (#27083)

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.


Edited by logan bender on
Replying to mmozeiko (#27084)

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.


Edited by Mārtiņš Možeiko on
Replying to userNameExists (#27086)

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.


Edited by logan bender on
Replying to mmozeiko (#27087)

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.


Replying to userNameExists (#27088)

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


Edited by logan bender on
Replying to mmozeiko (#27089)