If it is not static, the value will be lost whenever the function leaves. So in WM_CREATE, where you freshly assigned it, it's still available. Then WndProc returns, the variable goes out of scope, and soon some other function call overwrites the memory. When WndProc is called for WM_COMMAND, the variable has an effectively random value.
If it is static, it's preserved across function calls.
Static, as popular as it is (including Petzold's Programming Windows), can be problematic, though. If you create more than one window of the class, this will fail spectacularly. (Since it's your main window, creating more than one is not very common, but not unheard of either: window-per-document programs such as recent Word versions or Acrobat Reader do it.) We're talking about such weird errors as "I do something in one window, but it's the other one that changes."
Static variables in the WndProc are shared by all windows of that class. The second window would overwrite the variable containing the HWND of the first window's child with that of its own child.
The universally correct solution is to create a WindowData struct and add sizeof(WindowData*) bytes to the window-specific bytes, then access that.
Something like this (C++-style syntax, API names from memory):
const LPCTSTR CLASSNAME = TEXT("MyAppMainWindowClass");
const size_t OFF_WNDDATA = 0; // Would be adjusted if you stored more stuff in the window extra bytes.
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT InitWindow(HWND me, MainWindowData *mydata);
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.pfnWndProc = WndProc;
wcex.lpszClassName = CLASSNAME;
wcex.cbWndExtra = sizeof(MainWindowData*);
MainWindowData *GetMainWindowData(HWND hwnd)
LRESULT CALLBACK WndProc(HWND me, UINT msg, WPARAM w, LPARAM l)
MainWindowData *mydata = GetMainWindowData(me);
// The very first message ever sent, so it's the best place to allocate the data.
mydata = new MainWindowData;
SetWindowLongPtr(me, OFF_WNDDATA, reinterpret_cast<LONG_PTR>(mydata));
return 0; // Or 1, can't remember what indicates success.
return InitWindow(me, mydata);
// The last important part is to free the data again, else you'd get a memory leak if during the
// lifetime of your program you open and close main windows every now and then.
// WM_NCDESTROY is the very last message ever sent to a window.
return DefWindowProc(me, msg, w, l);
LRESULT InitWindow(HWND me, MainWindowData *mydata)
mydata->hListBox = CreateWindow("LISTBOX", NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER,
0, 0, 500, 300, hwnd, (HMENU) 500, hInstance, NULL);