用WINAPI创建的窗口未绘制对象有什么问题?

问题描述

我有我的窗口文件Window.h):

LRESULT CALLBACK MessageHandler(HWND,UINT,WParaM,LParaM);

class Window
{
private:
    HWND hWnd;
    HINSTANCE hInstance;
    bool running = true;
    const char* ID = "WINAPI_JVM64";
public:
    Window()
    {
        init();
    }

    virtual void draw(Gdiplus::Graphics*) = 0;

    void init()
    {
        hInstance = (HINSTANCE)GetModuleHandle(NULL);
        WNDCLASS wc;

        wc = {};
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = MessageHandler;
        wc.hInstance = hInstance;
        wc.hIcon = LoadIcon(NULL,IDI_WINlogo);
        wc.hCursor = LoadCursor(NULL,IDC_HAND);
        wc.hbrBackground = (HBrush)COLOR_WINDOW;
        wc.lpszClassName = ID;

        assert(RegisterClass(&wc));

        hWnd = CreateWindow(ID,"Title",WS_OVERLAPPEDWINDOW | WS_VISIBLE,200,400,NULL,hInstance,NULL);

        ShowCursor(true);
        SetForegroundWindow(hWnd);
        SetFocus(hWnd);
    }
    void run()
    {
        MSG msg;
        PeekMessage(&msg,hWnd,PM_REMOVE);
        while(running)
        {
            if(PeekMessage(&msg,PM_REMOVE))
            {
                if(msg.message == WM_QUIT)
                    running = false;

                TranslateMessage(&msg);
                dispatchMessage(&msg);
            }
            else
            {
                // Here,the draw function is called.
                PAINTSTRUCT ps;
                HDC hdc = BeginPaint(hWnd,&ps);
                Gdiplus::Graphics* g = Gdiplus::Graphics::FromHDC(hdc);
                draw(g);
                EndPaint(hWnd,&ps);
            }
        }
        UnregisterClass(ID,hInstance);
    }
};

文件main.cpp):

#include "Window.h"

LRESULT CALLBACK MessageHandler(HWND hWnd,UINT uMsg,WParaM wParam,LParaM lParam)
{
    switch(uMsg)
    {
    case WM_CLOSE:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd,uMsg,wParam,lParam);
    }
    return 0;
}

class AppWindow : public Window
{
public:
    void draw(Gdiplus::Graphics* g) override
    {
        Gdiplus::SolidBrush brown_brush(Gdiplus::Color(255,128,57,0));
        g->FillRectangle(&brown_brush,200);
    }
};

int main()
{
    Gdiplus::GdiplusstartupInput gdiplusstartupInput;
    ULONG_PTR gdiplusToken;
    Gdiplus::Gdiplusstartup(&gdiplusToken,&gdiplusstartupInput,nullptr);

    AppWindow w;
    w.run();

    Gdiplus::GdiplusShutdown(gdiplusToken);
    return 0;
}

我有一个问题,那就是它不会画出来!

它可以处理所有消息,一切都很好,但不会绘制。即使发送了WM_PAINT类型的消息,也没有任何反应。

您能发现问题吗?

我只希望一个窗口类具有一个可覆盖的draw()函数一个run()函数来处理所有事件,例如WM_LBUTTONDOWN。所有这些都工作正常,屏幕只是保持空白。

此外,我无法关闭窗口,当按下右上角的X按钮时,窗口将保持不变;只有在调整大小并快速按下X之后,它才会关闭

如您所见,我有一些很奇怪的行为,我不知道问题出在哪里。

解决方法

您的绘图逻辑位置错误。处理MessageHandler消息时,它必须在WM_PAINT内部。如果需要绘制窗口并且没有其他消息挂起,则PeekMessage()将生成一条WM_PAINT消息。您不能从WM_PAINT处理程序的外部在窗口上绘制。

此外,您正在为wc.hbrBackground中的init()分配错误的值。如果使用COLOR_WINDOW之类的颜色常量,则需要向其中添加1WNDCLASS documentation中对此进行了详细说明。

此外,在run()中,创建消息队列的第一个PeekMessage()会丢弃一条初始消息(如果一条消息正在等待处理),而该消息不会被您的调度循环处理。第一次通话应改用PM_NOREMOVE标志。

此外,请注意您的消息循环中的the dangers of filtering window messages

话虽如此,请尝试以下操作:

LRESULT CALLBACK MessageHandler(HWND,UINT,WPARAM,LPARAM);

class Window
{
private:
    HWND hWnd;
    HINSTANCE hInstance;
    const char* ID = "WINAPI_JVM64";

public:
    Window()
    {
        init();
    }

    ~Window()
    {
        cleanup();
    }

    virtual void draw(Gdiplus::Graphics*) = 0;

    void init()
    {
        hInstance = (HINSTANCE)GetModuleHandle(NULL);

        WNDCLASS wc{};
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = &MessageHandler;
        wc.hInstance = hInstance;
        wc.hIcon = LoadIcon(NULL,IDI_WINLOGO);
        wc.hCursor = LoadCursor(NULL,IDC_HAND);
        wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
        wc.lpszClassName = ID;

        assert(RegisterClass(&wc));

        hWnd = CreateWindow(ID,"Title",WS_OVERLAPPEDWINDOW | WS_VISIBLE,200,400,NULL,hInstance,this);
        assert(hWnd != NULL);

        ShowCursor(true);
        SetForegroundWindow(hWnd);
        SetFocus(hWnd);
    }

    void cleanup()
    {
        UnregisterClass(ID,hInstance);
    }

    void run()
    {
        MSG msg;
        PeekMessage(&msg,PM_NOREMOVE);

        while (GetMessage(&msg,0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
};
#include "Window.h"

LRESULT CALLBACK MessageHandler(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_NCCREATE:
        {
            Window *pThis = static_cast<Window*>(reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams);
            SetWindowLongPtr(hWnd,GWLP_USERDATA,reinterpret_cast<LONG_PTR>(pThis));
            break;
        }

        // DefWindowProc(WM_CLOSE) calls DestroyWindow(),// WM_CLOSE is not the right place to call PostQuitMessage()...
        //case WM_CLOSE:
        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        case WM_PAINT:
        {
            Window *pThis = reinterpret_cast<Window*>(GetWindowLongPtr(hWnd,GWLP_USERDATA));
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd,&ps);
            if (pThis)
            {
                Gdiplus::Graphics* g = Gdiplus::Graphics::FromHDC(hdc);
                pThis->draw(g);
                delete g;
            }
            EndPaint(hWnd,&ps);
            return 0;
        }
    }

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

class AppWindow : public Window
{
public:
    void draw(Gdiplus::Graphics* g) override
    {
        Gdiplus::SolidBrush brown_brush(Gdiplus::Color(255,128,57,0));
        g->FillRectangle(&brown_brush,200);
    }
};

int main()
{
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    Gdiplus::GdiplusStartup(&gdiplusToken,&gdiplusStartupInput,nullptr);

    AppWindow w;
    w.run();

    Gdiplus::GdiplusShutdown(gdiplusToken);
    return 0;
}