Home > Mobile >  Exception in Window Framework Win32 API
Exception in Window Framework Win32 API

Time:11-12

I'm new to Win32 API and want to create a window. I have a class (as follows) but I get an exception at the line return p_this->HandleMessages(msg, wParam, lParam);.

Window.h (I got this in some msdn website and modified to some extent):

#include "BaseWin.h"

template<class D_CLASS>
class Window
    :public BaseWin
{
public:
    int returnValue;
private:
    static constexpr LPCSTR className = "Best Window in the UNIVERSE!";
public:
    // declare this fn in the inherited class and use as wnd proc
    virtual LRESULT HandleMessages(UINT msg, WPARAM wParam, LPARAM lParam) = 0;


    static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        D_CLASS* p_this = nullptr;
        if (msg == WM_NCCREATE) {
            CREATESTRUCT* create = (CREATESTRUCT*)lParam;
            p_this = (D_CLASS*)create->lpCreateParams;
            SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)p_this);
        }
        else
            p_this = (D_CLASS*)GetWindowLongPtr(hwnd, GWLP_USERDATA);

        if (p_this)
            return p_this->HandleMessages(msg, wParam, lParam); // exception here
        else
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }

    Window(HWND parent, LPCSTR title, int id, const Point& pos, const Size& _size,
        int styles = WS_OVERLAPPEDWINDOW, int retVal = 0x45, int stylesEx = 0
    )
        :BaseWin(parent, pos, _size, id), returnValue(retVal)
    {
        WNDCLASSEX wc = { 0 };

        wc.cbSize = sizeof(wc);
        wc.style = CS_OWNDC;
        wc.lpfnWndProc = D_CLASS::WndProc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = GetModuleHandle(nullptr);
        wc.hIcon = nullptr;
        wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
        wc.hbrBackground = nullptr;
        wc.lpszMenuName = nullptr;
        wc.lpszClassName = className;
        wc.hIconSm = nullptr;

        RegisterClassEx(&wc);

        HMENU _id = nullptr;
        if (!((styles & WS_CHILD) != WS_CHILD || id == -1))
            _id = (HMENU)ID;

        hWnd = CreateWindowEx(
            stylesEx,
            className, title,
            styles,
            Position.x, Position.y, size.x, size.y,
            parent, _id, GetModuleHandle(nullptr), this
        );

    }
    
    ~Window()
    {
        UnregisterClass(className, GetModuleHandle(nullptr));
        Destroy();
    }
};

I'm using Visual Studio and whenever the line is reached is reached, a breakpoint is placed there are no details through which I can deduce the resaon (It's: main.exe has encountered a breakpoint).

main.cpp:

#include "Window.h"
#include "Button.h"

class MainWindow
    :public Window<MainWindow>
{
private:
    HMENU menuBar;
    HMENU menuIt;

    enum
    {
        BTN_KILL,
        M_QUIT,
        M_ADD_LOG
    };
public:
    MainWindow()
        :Window<MainWindow>(nullptr, "Useless Manger", -1, Point(CW_USEDEFAULT, CW_USEDEFAULT), Size(640, 480), normWinStyle)
    {
        Button* btn = new Button(hWnd, BTN_KILL, "Kill Me", Point(), Size(300, 100));

        menuBar = CreateMenu();
        menuIt = CreateMenu();
        AppendMenu(menuIt, MF_ENABLED, M_ADD_LOG, "Add Log");
        AppendMenu(menuBar, MF_ENABLED | MF_STRING, M_QUIT, "Quit");
        AppendMenu(menuBar, MF_POPUP, (UINT_PTR)menuIt, "Add");

        SetMenu(hWnd, menuBar);
    }


    LRESULT HandleMessages(UINT msg, WPARAM wParam, LPARAM lParam)
    {
        switch (msg)
        {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW   1));
            EndPaint(hWnd, &ps);
            break;
        }
        case WM_COMMAND:
            if (LOWORD(wParam) == BTN_KILL)
            {
                PostQuitMessage(this->returnValue);
                break;
            }
        }

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


int CALLBACK WinMain(
    HINSTANCE hInst,
    HINSTANCE h_p_Inst,
    LPSTR     nCmdLine,
    int       nCmdShow)
{
    MainWindow* window = new MainWindow();
    window->Show();

    MSG msg;

    while (GetMessage(&msg, nullptr, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

BaswWin.h is a class holding the current HWND, position, size, ID, etc with some functions like Show().

How do I fix this?

CodePudding user response:

MainWindow::HandleMessages is called before superclass Window<T> is fully constructed. Before Window<T> is fully constructed, entry for Window<T>::HandleMessages refers to special function witch reports that pure function was called. When Window<T> is fully constructed, this entry is replaced by MainWindow::HandleMessages. In C calling virtual functions when superclasses are still constructed needs special atention.

You can replace Window<T>::HandleMessage with unpure implementation.

virtual LRESULT HandleMessages(UINT msg, WPARAM wParam, LPARAM lParam)
{
    return DefWindowProc(hWnd, msg, wparam, lparam);
}

Initialize hWnd early, because WndProc can be called before CreateWindowEx returns.

static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    D_CLASS* p_this = nullptr;
    if (msg == WM_NCCREATE) {
        CREATESTRUCT* create = (CREATESTRUCT*)lParam;
        p_this = (D_CLASS*)create->lpCreateParams;
        // initialize early
        p_this->hWnd = hwnd;
        SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)p_this);
    }
    else
        p_this = (D_CLASS*)GetWindowLongPtr(hwnd, GWLP_USERDATA);

    if (p_this)
    // if you don't initialize early, skip when hWnd == nullptr
    // if (p_this && p_this->hWnd)
        return p_this->HandleMessages(msg, wParam, lParam);
    else
        return DefWindowProc(hwnd, msg, wParam, lParam);
}

Probably you would also miss WM_DESTROY message, when MainWindow class is already destroyed, but WM_DESTROY would be generated in Window<T>::~Window<T>.

Calling PostQuitMessage(this->returnValue); in MainWindow::~MainWindow could solve this problem.

  • Related