【问题标题】:WndProc as a class member, fails to quitWndProc 作为类成员,无法退出
【发布时间】:2014-03-15 19:59:27
【问题描述】:

我想让 WndProc 成为一个类成员函数,我找到了this article,所以我尝试将它应用到最简单的 Win32 程序中,它只是创建一个空白窗口,这是 Win32 的第一步。

int Myclass::Start(HINSTANCE hInstance, int nCmdShow)
{
    if (FAILED(InitWindow(hInstance, nCmdShow)))
        return 0;

    MSG msg = { 0 };
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;;
}

LRESULT Myclass::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    Myclass* pThis = nullptr; 

    if (message == WM_NCCREATE) {
        LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
        pThis = static_cast<Myclass*>(lpcs->lpCreateParams);

        SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));
    }
    else {
        pThis = reinterpret_cast<Myclass*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    }

    if(pThis)
        return pThis->RealWndProc(hWnd, message, wParam, lParam);

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

LRESULT Myclass::RealWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        EndPaint(hWnd, &ps);
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }

    return 0;
}

它运行良好,但是当我关闭窗口时,程序停留在消息循环中并且不会退出。

我发现WM_DESTROY没有传递给RealWndProc(),所以没有调用PostQuitMessage()。

如果我插入 if(WM_DESTROY == message) { PostQuitMessage(0);返回0; } 在 StaticWndProc 的最后一行之前,然后程序退出。但我不确定这是否是一个好方法。

如何让 RealWndProc() 使用 WM_DESTROY?

【问题讨论】:

    标签: c++ winapi wndproc


    【解决方案1】:

    我就是这样做的.. 工作得很好.. 我能在你的代码中想到的唯一问题是两个之一:

    • 您错过了 WindowProcedure 的调用约定。
    • 您忘记将“this”传递给 CreateWindowEx。

    还有代码:

    #include <windows.h>
    
    class Window
    {
        public:
            Window(LPCTSTR Title, LPCTSTR Class, DWORD dwStyleEx = 0, DWORD dwStyle = WS_OVERLAPPEDWINDOW,
                     POINT Location = {CW_USEDEFAULT, CW_USEDEFAULT}, int Width = CW_USEDEFAULT,
                     int Height = CW_USEDEFAULT, HWND Parent = HWND_DESKTOP, HMENU Menu = nullptr);
    
            int Loop();
    
        private:
            HWND WindowHandle;
            static LRESULT __stdcall WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam);
            LRESULT RealWindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam);
    };
    
    Window::Window(LPCTSTR Title, LPCTSTR Class, DWORD dwStyleEx, DWORD dwStyle, POINT Location, int Width, int Height, HWND Parent, HMENU Menu)
    {
        WNDCLASSEX WndClass =
        {
            sizeof(WNDCLASSEX), CS_DBLCLKS, Window::WindowProcedure,
            0, 0, GetModuleHandle(nullptr), LoadIcon(nullptr, IDI_APPLICATION),
            LoadCursor(nullptr, IDC_ARROW), HBRUSH(COLOR_BACKGROUND),
            nullptr, Class, LoadIcon(nullptr, IDI_APPLICATION)
        };
    
        if(RegisterClassEx(&WndClass))
        {
            WindowHandle = CreateWindowEx(dwStyleEx, Class, Title, dwStyle, Location.x, Location.y, Width, Height, Parent, Menu, GetModuleHandle(nullptr), this);
        }
    }
    
    LRESULT __stdcall Window::WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
    {
        Window* Data = nullptr;
    
        switch(Msg)
        {
            case WM_NCCREATE:
                {
                    CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
                    Data = static_cast<Window*>(pCreate->lpCreateParams);
                    SetWindowLongPtr(Hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(Data));
                    break;
                }
    
            default:
                {
                    Data = reinterpret_cast<Window*>(GetWindowLongPtr(Hwnd, GWLP_USERDATA));
                    break;
                }
        }
    
        return Data ? Data->RealWindowProcedure(Hwnd, Msg, wParam, lParam) : DefWindowProc(Hwnd, Msg, wParam, lParam);
    }
    
    LRESULT Window::RealWindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
    {
        switch(Msg)
        {
            case WM_DESTROY:
                PostQuitMessage(0);
            break;
    
            default:
                return DefWindowProc(Hwnd, Msg, wParam, lParam);
        }
    
        return 0;
    }
    
    int Window::Loop()
    {
        MSG Messages = {nullptr};
        ShowWindow(WindowHandle, SW_SHOW);
        while(GetMessage(&Messages, nullptr, 0, 0))
        {
            TranslateMessage(&Messages);
            DispatchMessage(&Messages);
        }
        return Messages.wParam;
    }
    
    int main()
    {
        Window w("TItle", "Class");
        return w.Loop();
    }
    

    【讨论】:

    • 你是对的!将“this”传递给 CreatWindow 解决了这个问题!我认为 InitWindow() 是微不足道的,所以我省略了它,但我错了。我刚刚发现那篇文章中甚至提到了它......哦,我的......(叹气)非常感谢!
    【解决方案2】:

    你应该抓住WM_CLOSE并让它调用DestroyWindow()来触发WM_DESTROY

    case WM_CLOSE:
        DestroyWindow(hWnd);
        break;
    

    另外,您的 StaticWndProc() 方法使用了错误的调用约定。它必须使用__stdcall调用约定,由WINAPICALLBACK宏包装,例如:

    LRESULT CALLBACK Myclass::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    

    【讨论】:

    • 据我所知,你所说的 WM_CLOSE 是由 DefWindowProc 完成的,不是吗?对于调用约定,我在头文件中写了“CALLBACK”,但我没有提到它,所以这是我的错误。 :)
    猜你喜欢
    • 1970-01-01
    • 2018-12-24
    • 1970-01-01
    • 2012-09-16
    • 2016-02-25
    • 2013-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多