Userdata, and the main window procedure. (I'm having trouble)

This crap has been frustrating me for some time. I started to get rid of the globals in my program, the only problem was with callbacks as usual... Casey helped me out by suggesting SetWindowLongPtr. Now I don't know if it's the way I structured my program, most likely because I'm an idiot, but here's my understanding's breakdown.

- I need to create a window, obviously.
- When you create a window, it calls resize window
- My resize window is dependent on my state struct
- I need to create and initialize my state struct before I create my window obviously
- userdata is null when it creates the window, so I can't get my state inside of the window proc callback
- I need SetWindowLongPtr to be before I create the window
- SetWindowLongPtr, as far as I know NEEDS the HWND window
- GAHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
LRESULT CALLBACK 
mainWindowProc(HWND window, 
               UINT message, 
	       WPARAM wParam, 
	       LPARAM lParam)
{
    LONG_PTR userdata = GetWindowLongPtrA(window, GWLP_USERDATA);
    State *state = (State *)userdata; // <- Null pointer given right here...

    switch(message)
    {
        case WM_SIZE:
        {
            int width = LOWORD(lParam);
    	    int height = HIWORD(lParam);

            RECT windowRectangle;
            GetClientRect(window, &windowRectangle);
            u32 drawingWindowWidth = windowRectangle.right - windowRectangle.left;
            u32 drawingWindowHeight = windowRectangle.bottom - windowRectangle.top;

            windowResize(state, window, (u32)wParam, drawingWindowWidth, drawingWindowHeight); // <- Null pointer passed right here
        }break;

        ...
    }
    ...
}

int CALLBACK 
WinMain(HINSTANCE instance, 
        HINSTANCE prevInstance, 
	LPSTR commandLine, 
	int showCommand)
{
    // blah blah creating window classes
    
    State state = {};
    windowLoad(&state, commandLine);
    HWND window = CreateTheDamnWindowFunction([insertparamshere]);

    SetWindowLongPtrA(window, GWLP_USERDATA, (LONG_PTR)&state);
    
    .... (later on in the code)
    while(PeekMessage(&message, 0, 0, 0, PM_REMOVE))
    {
        TranslateMessage(&message);
	DispatchMessageA(&message);
    }

    ....
}


(in another file)

void
// Todo: Do we need to pass the HWND struct into here?
windowResize(State *state, HWND window, u32 wParam, int width, int height)
{
    // NOTE: wParam
    // SIZE_MAXHIDE   := 4 | Message is sent to all pop-up windows when some other window is maximized
    // SIZE_MAXSHOW   := 3 | Message is sent to all pop up windows when some other window has been restored to its former size
    // SIZE_MAXIMIZED := 2 | The window has been maximized
    // SIZE_MINIMIZED := 1 | The window has been minimized
    // SIZE_RESTORED  := 0 | The window has been resized, but neither the SIZE_MINIMIZED nor SIZE_MAXIMIZED value applies

    state->windowInfo.width = width; // <- WHABAM U TRIED TO USE A NULL POINTER, PREPARE FOR DESTRUCTION *Crash, ded program*
    state->windowInfo.height = height;
    bitmapResize(&state->backBuffer, width, height);
}


Does anyone have any thoughts on this? I'm sure other people have at least run into this before.
When you call CreateWindow, you can pass userdata as the final paramter (lParam). This will come through in the initial message to your window, WM_CREATE, as the lpCreateParams member of the CREATESTRUCT pointer that gets passed in your LPARAM. For example, you could put something like this at the top of your window callback:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    your_type *This;
    if(Message == WM_CREATE)
    {
        CREATESTRUCT *CreateStruct = (CREATESTRUCT *)LParam;
        This = (your_type *)CreateStruct->lpCreateParams;
        SetWindowLongPtr(Window, GWLP_USERDATA, (LONG_PTR)This);
    }
    else
    {
        This = (your_type *)GetWindowLongPtr(Window, GWLP_USERDATA);
    }


and then you'd have a valid "This" pointer for the callback. You just have to make sure you pass a valid "your_type" pointer as the last parameter of CreateWindow when you make it.

Does that help?

- Casey
I'll add a few things to Casey's suggestion. There are few messages that are sent before WM_CREATE - WM_GETMINMAXINFO, WM_NCCREATE, and WM_NCCALCSIZE. So GWL_USERDATA will be not yet set and "This" will be NULL for these messages. Be careful if you are interested in processing these messages.
Holy shit, that seems more convoluted than it's suppose to be (CREATESTRUCT?? what type of datatype is that). Anyway I got it, thanks Casey it's back to working order. Sorry for having you spend more time than you ought to. And thanks mmozeiko, that's good info. I'll leave a note next to my code.

Sorry if I seemed a bit anxious and snappy, getting a bit frustrated with this stuff. Not exactly the most experienced here. Take it easy guys.