You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在WM_COMMAND中调用GDI+绘制线条无效,求解决方案

点击按钮触发WM_COMMAND调用GDI+绘图不显示的问题

我尝试通过点击按钮触发WM_COMMAND消息,调用GDI+绘制一条线条,但点击按钮后线条并未显示。不过在WM_PAINT消息中绘制线条是有效的,我需要实现点击按钮就显示线条的效果。以下是我的代码:

#include <Windows.h>
#include <gdiplus.h>

#pragma comment(lib, "gdiplus.lib")

#define onButtonClicked 1

using namespace Gdiplus;

WNDCLASS NewWindowClass(HBRUSH BGColor, HCURSOR Cursor, HINSTANCE hInt, HICON Icon, LPCWSTR Name, WNDPROC Procedure);
LRESULT CALLBACK mainProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR args, int ncmdshow) {

    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);

    WNDCLASS mainClass = NewWindowClass((HBRUSH)COLOR_WINDOW, LoadCursor(NULL, IDC_ARROW), hInst, LoadIcon(NULL, IDI_APPLICATION), L"Window", mainProcedure);

    if (!RegisterClassW(&mainClass)) return -1;

    MSG mainMessage = { 0 };
    CreateWindow(L"Main", L"Main window", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 500, 500, 500, 400, NULL, NULL, NULL, NULL);

    while (GetMessage(&mainMessage, NULL, NULL, NULL)) {
        TranslateMessage(&mainMessage);
        DispatchMessage(&mainMessage);
    }

    GdiplusShutdown(gdiplusToken);
    return 0;
}

WNDCLASS NewWindowClass(HBRUSH BGColor, HCURSOR Cursor, HINSTANCE hInt, HICON Icon, LPCWSTR Name, WNDPROC Procedure) {

    WNDCLASS NWC = { 0 };

    NWC.hbrBackground = BGColor;
    NWC.hCursor = Cursor;
    NWC.hInstance = hInt;
    NWC.hIcon = Icon;
    NWC.lpszClassName = Name;
    NWC.lpfnWndProc = Procedure;

    return NWC;
}

LRESULT CALLBACK mainProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {

    HDC hdc;
    PAINTSTRUCT ps;

    switch (msg) {
    case WM_CREATE:
        CreateWindowA("button", "Draw", WS_VISIBLE | WS_CHILD, 10, 10, 50, 20, hWnd, (HMENU)onButtonClicked, NULL, NULL);
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    case WM_COMMAND:
        switch (wp) {
        case onButtonClicked:
            hdc = BeginPaint(hWnd, &ps);
            Graphics gf(hdc);
            Pen pen(Color(0, 0, 0));
            gf.DrawLine(&pen, 10, 160, 50, 200);
            EndPaint(hWnd, &ps);
            break;
        }
        break;

    default:
        return DefWindowProc(hWnd, msg, wp, lp);
    }
}

问题分析

不能在WM_COMMAND中直接使用BeginPaint/EndPaint组合来绘图。BeginPaint仅在处理WM_PAINT消息时有效,它的作用是获取窗口的无效区域并初始化绘图环境;在WM_COMMAND中调用这组函数,系统不会正确渲染绘制内容——要么绘制的图形会被后续的窗口重绘操作覆盖,要么根本不会被输出到屏幕上。

GDI+函数本身可以在非WM_PAINT的消息中调用,但必须通过GetDC获取窗口DC,不过这种方式绘制的内容是临时的,窗口最小化、被遮挡恢复后会丢失,不符合持久化显示的需求。

正确实现方案

Windows的标准绘图模型要求:所有需要持久化显示的绘图操作都应该放在WM_PAINT消息处理中。点击按钮时只需要触发窗口重绘请求,让系统自动调用WM_PAINT完成绘图。具体步骤:

  1. 定义一个状态变量标记是否需要绘制线条
  2. 按钮点击时设置状态为true,并调用InvalidateRect触发窗口重绘
  3. 在WM_PAINT中根据状态变量决定是否绘制线条

修改后的完整代码

#include <Windows.h>
#include <gdiplus.h>

#pragma comment(lib, "gdiplus.lib")

#define onButtonClicked 1

using namespace Gdiplus;

WNDCLASS NewWindowClass(HBRUSH BGColor, HCURSOR Cursor, HINSTANCE hInt, HICON Icon, LPCWSTR Name, WNDPROC Procedure);
LRESULT CALLBACK mainProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);

// 标记是否需要绘制线条
bool drawLine = false;

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR args, int ncmdshow) {

    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);

    WNDCLASS mainClass = NewWindowClass((HBRUSH)COLOR_WINDOW, LoadCursor(NULL, IDC_ARROW), hInst, LoadIcon(NULL, IDI_APPLICATION), L"Window", mainProcedure);

    if (!RegisterClassW(&mainClass)) return -1;

    MSG mainMessage = { 0 };
    // 修正窗口类名参数,和注册的类名一致
    CreateWindow(L"Window", L"Main window", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 500, 500, 500, 400, NULL, NULL, hInst, NULL);

    while (GetMessage(&mainMessage, NULL, NULL, NULL)) {
        TranslateMessage(&mainMessage);
        DispatchMessage(&mainMessage);
    }

    GdiplusShutdown(gdiplusToken);
    return 0;
}

WNDCLASS NewWindowClass(HBRUSH BGColor, HCURSOR Cursor, HINSTANCE hInt, HICON Icon, LPCWSTR Name, WNDPROC Procedure) {

    WNDCLASS NWC = { 0 };

    NWC.hbrBackground = BGColor;
    NWC.hCursor = Cursor;
    NWC.hInstance = hInt;
    NWC.hIcon = Icon;
    NWC.lpszClassName = Name;
    NWC.lpfnWndProc = Procedure;

    return NWC;
}

LRESULT CALLBACK mainProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {

    HDC hdc;
    PAINTSTRUCT ps;

    switch (msg) {
    case WM_CREATE:
        CreateWindowA("button", "Draw", WS_VISIBLE | WS_CHILD, 10, 10, 50, 20, hWnd, (HMENU)onButtonClicked, NULL, NULL);
        break;

    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        Graphics gf(hdc);
        if (drawLine) {
            Pen pen(Color(0, 0, 0));
            gf.DrawLine(&pen, 10, 160, 50, 200);
        }
        EndPaint(hWnd, &ps);
        break;

    case WM_COMMAND:
        switch (wp) {
        case onButtonClicked:
            drawLine = true;
            // 触发窗口重绘,让WM_PAINT处理绘图逻辑
            InvalidateRect(hWnd, NULL, TRUE);
            break;
        }
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hWnd, msg, wp, lp);
    }
    return 0;
}

额外修正说明

原代码中CreateWindow的第一个参数使用了L"Main",但注册的窗口类名是L"Window",这会导致窗口创建失败,修改后保持类名一致。

内容的提问来源于stack exchange,提问作者Ogurchik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 15:23:25