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

如何在Win32 API(C++)中获取WM_PAINT脏矩形的客户区坐标?

解决Win32中获取客户区绝对坐标脏区域的问题

要获取客户区坐标系下的脏区域(更新区域)坐标,不能依赖BeginPaint返回的PAINTSTRUCT——因为BeginPaint会将DC的裁剪区原点偏移到脏区域的左上角,导致rcPaint的left/top始终为0。正确的做法是在调用BeginPaint之前,通过GetUpdateRgn获取原始更新区域:

核心实现步骤

  • 调用GetUpdateRgn获取窗口当前的更新区域,该区域的坐标基于客户区绝对坐标系。
  • 通过GetRgnBox将区域转换为包围矩形(如果是多块不连续脏区域,会返回整体的边界框;若需处理每块独立区域,可使用EnumRgn枚举)。
  • 后续再调用BeginPaint/EndPaint完成标准绘制流程,避免窗口一直处于需要重绘的状态。

代码示例

case WM_PAINT:
{
    // 先获取原始更新区域(客户区绝对坐标)
    HRGN hUpdateRgn = CreateRectRgn(0, 0, 0, 0);
    int rgnResult = GetUpdateRgn(hwnd, hUpdateRgn, FALSE); // FALSE:不清除更新区域

    if (rgnResult != ERROR && rgnResult != NULLREGION)
    {
        RECT dirtyClientRect;
        GetRgnBox(hUpdateRgn, &dirtyClientRect);

        // 转换为回调所需的参数
        int x = dirtyClientRect.left;
        int y = dirtyClientRect.top;
        unsigned int width = static_cast<unsigned int>(dirtyClientRect.right - dirtyClientRect.left);
        unsigned int height = static_cast<unsigned int>(dirtyClientRect.bottom - dirtyClientRect.top);

        // 这里添加获取像素数据的逻辑(比如通过GetDIBits从DC读取)
        void* pixelBuffer = ...;
        std::size_t bufferSize = ...;

        // 调用你的重绘回调
        paint_callback(x, y, width, height, pixelBuffer, bufferSize);
    }

    DeleteObject(hUpdateRgn);

    // 完成标准WM_PAINT流程,清除更新标记
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);
    // 这里可保留必要的默认绘制逻辑(如果需要)
    EndPaint(hwnd, &ps);

    return 0;
}

处理多块不连续脏区域

如果窗口的脏区域是多个独立矩形,GetRgnBox只能获取整体边界框。若需要针对每块区域单独调用回调,可使用EnumRgn枚举区域内的所有矩形:

// 枚举回调函数
int CALLBACK EnumRgnProc(LPRECT lpRect, LPARAM lParam)
{
    auto callback = reinterpret_cast<paint_callback_t*>(lParam);
    int x = lpRect->left;
    int y = lpRect->top;
    unsigned int w = static_cast<unsigned int>(lpRect->right - lpRect->left);
    unsigned int h = static_cast<unsigned int>(lpRect->bottom - lpRect->top);

    // 同样需获取对应区域的像素数据后调用回调
    void* pixels = ...;
    std::size_t size = ...;
    (*callback)(x, y, w, h, pixels, size);
    return TRUE; // 继续枚举
}

// 在WM_PAINT中替换GetRgnBox的部分:
EnumRgn(hdc, hUpdateRgn, EnumRgnProc, reinterpret_cast<LPARAM>(&paint_callback));

关键注意事项

  • GetUpdateRgn的第三个参数bErase必须设为FALSE,否则会提前清除更新区域,导致BeginPaint无法正确处理。
  • 使用完创建的区域对象后,必须调用DeleteObject释放资源,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 19:05:03