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

C++中如何撤销GDI+直接在屏幕上完成的绘制内容?

解决方案:快速清除屏幕上的GDI+绘制内容

直接通过GetDC(0)绘制到屏幕DC的内容属于临时覆盖,系统不会追踪这些绘制操作,因此需要针对性地刷新绘制区域,或者改用分层窗口作为绘制载体来简化清除逻辑。

方法一:记录绘制区域并针对性刷新

核心思路是保存所有绘制图形的边界区域,清除时通知系统重新刷新这些区域,让底层窗口重绘覆盖你的绘制内容。

步骤1:记录绘制区域

在绘制时,把每个图形的边界矩形保存下来(注意笔宽的影响,比如3像素的笔需要把矩形向外扩展2像素以覆盖完整线条):

#include <vector>
#include <windows.h>
#include <gdiplus.h>

using namespace Gdiplus;

// 保存所有绘制区域的列表(建议作为全局或类成员变量)
std::vector<RECT> g_drawnRegions;

// 绘制代码示例
void DrawOnScreen() {
    HDC hScreenDC = GetDC(0);
    Graphics graphics(hScreenDC);

    Pen gp(Color::DarkOliveGreen, 3);
    SolidBrush gpb(Color::DarkOliveGreen);

    // 绘制第一个椭圆并记录区域
    Rect ellipse1(matpos[0][0] - 5, matpos[0][1] - 5, 10, 10);
    graphics.FillEllipse(&gpb, ellipse1);
    g_drawnRegions.push_back({ellipse1.X, ellipse1.Y, ellipse1.X + ellipse1.Width, ellipse1.Y + ellipse1.Height});

    // 绘制第二个椭圆并记录区域
    Rect ellipse2(matpos[1][0] - 5, matpos[1][1] - 5, 10, 10);
    graphics.FillEllipse(&gpb, ellipse2);
    g_drawnRegions.push_back({ellipse2.X, ellipse2.Y, ellipse2.X + ellipse2.Width, ellipse2.Y + ellipse2.Height});

    // 绘制矩形并记录区域(扩展笔宽范围)
    Rect rect = get_rect_from_points(Rect(matpos[0][0], matpos[0][1], matpos[1][0], matpos[1][1]));
    graphics.DrawRectangle(&gp, rect);
    RECT rectRegion = {
        rect.X - 2,
        rect.Y - 2,
        rect.X + rect.Width + 2,
        rect.Y + rect.Height + 2
    };
    g_drawnRegions.push_back(rectRegion);

    ReleaseDC(0, hScreenDC);
}

步骤2:清除绘制内容

遍历保存的区域,调用RedrawWindow强制刷新这些区域:

void ClearAllDrawings() {
    for (const RECT& rc : g_drawnRegions) {
        // 通知系统立即刷新指定区域,让底层窗口重绘
        RedrawWindow(NULL, &rc, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
    }
    g_drawnRegions.clear(); // 清空记录
}

方法二:改用分层窗口作为绘制载体

直接绘制到屏幕DC的方式缺乏可控性,改用分层透明窗口作为绘制容器,清除时只需刷新或隐藏窗口即可,逻辑更简洁稳定。

步骤1:创建分层窗口

// 注册窗口类(程序初始化时执行)
void RegisterOverlayWindowClass(HINSTANCE hInstance) {
    WNDCLASSEX wc = {0};
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.lpfnWndProc = DefWindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = L"OverlayWindowClass";
    RegisterClassEx(&wc);
}

// 创建分层窗口
HWND CreateOverlayWindow(HINSTANCE hInstance) {
    HWND hWnd = CreateWindowEx(
        WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
        L"OverlayWindowClass",
        NULL,
        WS_POPUP,
        0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
        NULL, NULL, hInstance, NULL
    );

    // 设置窗口背景为完全透明(颜色键为黑色)
    SetLayeredWindowAttributes(hWnd, RGB(0,0,0), 0, LWA_COLORKEY);
    ShowWindow(hWnd, SW_SHOW);
    return hWnd;
}

步骤2:在分层窗口上绘制

// 绘制到分层窗口
void DrawOnOverlay(HWND hOverlayWnd) {
    HDC hWndDC = GetDC(hOverlayWnd);
    Graphics graphics(hWndDC);

    Pen gp(Color::DarkOliveGreen, 3);
    SolidBrush gpb(Color::DarkOliveGreen);

    // 绘制逻辑和之前一致
    graphics.FillEllipse(&gpb, matpos[0][0] - 5, matpos[0][1] - 5, 10, 10);
    graphics.FillEllipse(&gpb, matpos[1][0] - 5, matpos[1][1] - 5, 10, 10);
    graphics.DrawRectangle(&gp, get_rect_from_points(Rect(matpos[0][0], matpos[0][1], matpos[1][0], matpos[1][1])));

    ReleaseDC(hOverlayWnd, hWndDC);
}

步骤3:清除绘制内容

只需刷新窗口(背景透明,刷新后绘制内容消失)或直接隐藏窗口:

void ClearOverlayDrawings(HWND hOverlayWnd) {
    // 方式1:刷新窗口,清除所有绘制
    InvalidateRect(hOverlayWnd, NULL, TRUE);
    UpdateWindow(hOverlayWnd);

    // 方式2:直接隐藏窗口(退出前销毁即可)
    // ShowWindow(hOverlayWnd, SW_HIDE);
}

为什么之前的方法无效?

  • 覆盖透明元素:透明画刷不会修改屏幕像素,因此无法覆盖原有绘制内容。
  • 直接调用RedrawWindow(NULL, NULL, ...):刷新整个屏幕效率极低,且系统可能不会立即处理,导致你的绘制区域未被及时覆盖。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 13:03:15