【问题标题】:Exception in Window Framework Win32 APIWindow Framework Win32 API 中的异常
【发布时间】:2021-12-23 23:09:57
【问题描述】:

我是 Win32 API 的新手,想创建一个窗口。我有一堂课(如下),但在return p_this->HandleMessages(msg, wParam, lParam); 行出现异常。

Window.h(我在某个msdn网站上得到的,做了一些修改):

#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();
    }
};

我正在使用 Visual Studio,每当到达该行时,就会放置一个断点,没有可以推断原因的详细信息(它是:main.exe has encountered a breakpoint)。

ma​​in.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 是一个包含当前HWND、position、size、ID 等的类,具有Show() 等一些功能。

我该如何解决这个问题?

【问题讨论】:

  • 你遇到了什么异常?
  • 我不知道这是否完全是一个例外,但 Visual Studio 在该行放置一个断点并在详细信息中显示main.exe has encountered a breakpoint。还会弹出一个窗口,显示 abort() 已被调用。
  • p_this 是否为空?是 0xcccccccc 还是 0xcdcdcdcd 还是 0xFEEEFEEE 还是此答案中列出的模式之一:https://stackoverflow.com/a/127404/487892
  • 您所有的Window&lt;T&gt; 类共享相同的类名,因此它们会相互冲突。 (还有,什么是BaseWin?代码不全。)
  • @drescherjm p_this 给出一个随机值,例如 0x1177a90。我还尝试使用不带-mwindows 标志的g++ 进行编译,它在cmd 中显示pure virtual method called terminate called without an active exception。

标签: c++ winapi


【解决方案1】:

MainWindow::HandleMessages 在超类 Window&lt;T&gt; 完全构造之前被调用。在Window&lt;T&gt; 完全构造之前,Window&lt;T&gt;::HandleMessages 的条目是指特殊函数,女巫报告称调用了纯函数。当Window&lt;T&gt; 完全构造时,此条目将替换为MainWindow::HandleMessages。 在 C++ 中,在超类仍然构造时调用虚函数需要特别注意。

你可以用不纯的实现替换Window&lt;T&gt;::HandleMessage。

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

提前初始化hWnd,因为WndProc可以在CreateWindowEx返回之前被调用。

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);
}

您可能还会错过WM_DESTROY 消息,此时MainWindow 类已被销毁,但WM_DESTROY 将在Window&lt;T&gt;::~Window&lt;T&gt; 中生成。

在MainWindow::~MainWindow 中调用PostQuitMessage(this-&gt;returnValue); 可以解决这个问题。

【讨论】:

  • 非常感谢您的帮助。顺便说一句,我确实收到了 WM_DESTROY 和 WM_CLOSE 消息,不需要在析构函数中这样做。
猜你喜欢
  • 1970-01-01
  • 2023-03-04
  • 2016-09-11
  • 1970-01-01
  • 2015-08-23
  • 2011-03-12
  • 1970-01-01
  • 2018-02-23
  • 1970-01-01
相关资源
最近更新 更多