0

I am creating a window wrapper class in c++ but I cannot seem to fix this bug. it seems that when my window process callback gets called it is not retrieving the this pointer I inputted for the extra information when creating the window. I did a little bit of debugging to find out that when I reinterpret it to a Window* it is NULL and every variable is uninitialized. here is Window.h

    #pragma once

    #include <Windows.h>
    #include <string>




    class Window
    {
    private:

        class WindowClass
        {
        private:
            static WindowClass wndClass;


            WindowClass();
            HINSTANCE hInst;
            const char* className = "Sadie Game Engine";

            WindowClass(WindowClass&) = delete;
            WindowClass operator=(WindowClass&) = delete;

            ~WindowClass();
        public:
            static WindowClass& getInstance();
            HINSTANCE getHInstance();

            const char* getClassName();
        };

    private:
        const char* windowTitle;
        int width, height;
        int windowX, windowY;
        HWND windowHandle;
    public:

        
        bool keyStates[256];

        Window(int width, int height, int x, int y, const char* title);
    
        void setWindowTitle(std::string str);

        static LRESULT CALLBACK handleMsgCallBack(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
        LRESULT handleMsg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

        ~Window();


    

    };


and here is Window.cpp


    #include "Window.h"



    /// 
    /// WindowClass
    /// 
    Window::WindowClass Window::WindowClass::wndClass;


    Window::WindowClass::WindowClass()
    {
        hInst = GetModuleHandleA(nullptr);

        WNDCLASSEXA wc = { 0 };
        wc.cbSize = sizeof(wc);
        wc.style = CS_OWNDC;
        wc.lpfnWndProc = handleMsgCallBack;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = hInst;
        wc.lpszClassName = className;
        wc.hIcon = nullptr;
        wc.hIconSm = nullptr;
        wc.hCursor = nullptr;
        wc.hbrBackground = nullptr;
        wc.lpszMenuName = nullptr;
    

        RegisterClassExA(&wc);


    }

    Window::WindowClass::~WindowClass()
    {
        UnregisterClassA(className, hInst);
    }

    Window::WindowClass& Window::WindowClass::getInstance()
    {
        return wndClass;
    }


    HINSTANCE Window::WindowClass::getHInstance()
    {
        return hInst;
    }

    const char* Window::WindowClass::getClassName()
    {
        return className;
    }


    ///
    /// Window
    /// 



    Window::Window(int _width, int _height, int x, int y, const char* title)
    {
    
        width = _width;
        height = _height;
        windowX = x;
        windowY = y;
        windowTitle = title;


        RECT wr;
        wr.left = 100;
        wr.right = width + wr.left;
        wr.top = 100;
        wr.bottom = height + wr.top;
        AdjustWindowRect(&wr, WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU, FALSE);



        windowHandle = CreateWindowExA(0l, WindowClass::getInstance().getClassName(),
            windowTitle, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
            windowX, windowY, width, height, nullptr, nullptr, WindowClass::getInstance().getHInstance(), this);



        ShowWindow(windowHandle, SW_SHOW);




    }

    void Window::setWindowTitle(std::string str)
    {
        SetWindowTextA(windowHandle, str.c_str());


    }

    LRESULT Window::handleMsgCallBack(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    

        Window* const windowPtr = reinterpret_cast<Window*>(GetWindowLongPtrA(hWnd, GWLP_USERDATA));





        return windowPtr->handleMsg(hWnd, msg, wParam, lParam);

    
    }

    LRESULT Window::handleMsg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {

    



        switch (msg)
        {
    
        case WM_SYSKEYDOWN:

        case WM_KEYDOWN:



            keyStates[wParam] = true;

            break;
        case WM_SYSKEYUP:

        case WM_KEYUP:


            keyStates[wParam] = false;

            break;

        case WM_CHAR:

            

            break;
        case WM_CLOSE:
            PostQuitMessage(0);
            return 0;
        }


        return DefWindowProcA(hWnd, msg, wParam, lParam);
    }

    Window::~Window()
    {
        DestroyWindow(windowHandle);
    }



Any help would be appreciated.

Jaden
  • 55
  • 6
  • 3
    and where you call ***Set**WindowLongPtrA(hWnd, GWLP_USERDATA, (LONG_PTR)This)* ? – RbMm Jan 22 '21 at 08:12

1 Answers1

3

As RbMm said, You did not call the SetWindowLongPtr function

Just add the following code:

case WM_NCCREATE:
{
    LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
    Window* w = (Window*)pcs->lpCreateParams;
    ::SetWindowLongPtr(hWnd,GWLP_USERDATA,reinterpret_cast<LONG_PTR>(w));
    break;
}
Zeus
  • 3,703
  • 3
  • 7
  • 20
  • why not on `WM_NCCREATE` – RbMm Jan 22 '21 at 08:49
  • @RbMm Yes, my mistake. I have corrected it. – Zeus Jan 22 '21 at 08:50
  • 2
    this not mistake. possible do this and on `WM_CREATE` too. both receive `LPCREATESTRUCT` in *lParam*. simply i think - set `GWLP_USERDATA` early as possible is better. `WM_NCCREATE` will be before `WM_CREATE` and because this better set on this message. possible do this and on first received message, which can be before `WM_NCCREATE`, but this already bit more complex – RbMm Jan 22 '21 at 08:56
  • Just note that `WM_NCCREATE` is not always the 1st message received. For a top-level window, `WM_GETMINMAXINFO` can be received before `WM_NCCREATE` (see https://stackoverflow.com/questions/1741296/). If you really needed to handle that initial message, you will have to either 1) use a thunk for the window procedure, where the `this` pointer is stored inside the thunk itself; or 2) store the `this` pointer in a global (or better, a thread-local) variable before calling `CreateWindow/Ex()`. – Remy Lebeau Jan 22 '21 at 17:49
  • As an extension to Remy's 2nd suggestion, you could also install a local [CBT hook](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644977(v=vs.85)) and store the instance pointer into the window memory in a `HCBT_CREATEWND` handler. This still requires that you temporarily store the instance pointer in a global or thread-local variable. It's also a fair bit safer to allocate `cbWndExtra` memory to store the pointer rather than placing it into `GWLP_USERDATA`. – IInspectable Jan 22 '21 at 20:51