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

WinAPI双缓冲实现异常,屏幕频繁闪烁问题排查求助

WinAPI双缓冲闪烁问题排查与修复

问题描述

我正在学习WinAPI双缓冲技术,运行下方代码后屏幕出现严重闪烁现象。我认为自己的实现逻辑无误,但不知问题出在哪里?
绘制逻辑为:将memdc1与memdc2绘制到memdc,再将memdc绘制到hdc。

原代码

switch (iMsg)
{
case WM_CREATE:     
    hdc = GetDC(hwnd);
    GetClientRect(hwnd, &clientRt);
    PlaceCenterScreen(hwnd);
    SetTimer(hwnd, 1, 10, NULL);
    hMixBit = CreateCompatibleBitmap(hdc, 819, 614);
    hHouseBit = (HBITMAP)LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_HOUSE));
    hTigerBit = (HBITMAP)LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_TIGER));
    ReleaseDC(hwnd, hdc);
    break;  
case WM_TIMER:
    posX += 3;
    InvalidateRgn(hwnd, NULL, true);
    break;
case WM_PAINT:
    hdc = BeginPaint(hwnd, &ps); 

    memdc = CreateCompatibleDC(hdc);
    memdc1 = CreateCompatibleDC(memdc);
    memdc2 = CreateCompatibleDC(memdc);

    oldHouseBit = (HBITMAP)SelectObject(memdc1, hHouseBit);
    oldTigerBit = (HBITMAP)SelectObject(memdc2, hTigerBit);
    oldMixBit = (HBITMAP)SelectObject(memdc, hMixBit);

    BitBlt(memdc, 0, 0, 819, 614, memdc1, 0, 0, SRCCOPY);
    BitBlt(memdc, posX, 0, 67, 47, memdc2, 0, 0, SRCCOPY);
    BitBlt(hdc, 0, 0, 819, 614, memdc, 0, 0, SRCCOPY);

    SelectObject(memdc1, oldHouseBit);
    SelectObject(memdc2, oldTigerBit);      
    SelectObject(memdc, oldMixBit);

问题分析与修复方案

1. 系统自动擦除背景导致闪烁

InvalidateRgn(hwnd, NULL, true)的第三个参数设为true时,系统会在重绘前自动擦除窗口背景,擦除动作和后续双缓冲绘制的间隔会直接造成视觉闪烁。将参数改为false,让双缓冲统一绘制完整画面,避免系统自动擦除:

InvalidateRgn(hwnd, NULL, false);

2. 未销毁临时DC引发资源泄漏

WM_PAINT中创建的memdc、memdc1、memdc2没有调用DeleteDC销毁,会持续消耗GDI资源,长时间运行后资源不足会引发绘制异常和闪烁。恢复GDI对象后必须销毁这些临时DC:

SelectObject(memdc1, oldHouseBit);
SelectObject(memdc2, oldTigerBit);      
SelectObject(memdc, oldMixBit);

// 新增:销毁临时DC
DeleteDC(memdc2);
DeleteDC(memdc1);
DeleteDC(memdc);

3. 缺少EndPaint导致重复重绘

WM_PAINT消息中调用BeginPaint后必须调用EndPaint,否则系统会认为窗口仍需重绘,不断发送WM_PAINT消息,导致重复绘制和闪烁。在绘制完成后添加:

EndPaint(hwnd, &ps);

4. 可选优化:提前创建静态DC

每次WM_PAINT都创建memdc1、memdc2会增加资源开销和绘制延迟,可将这两个DC的创建移到WM_CREATE中只初始化一次:

case WM_CREATE:     
    hdc = GetDC(hwnd);
    GetClientRect(hwnd, &clientRt);
    PlaceCenterScreen(hwnd);
    SetTimer(hwnd, 1, 10, NULL);
    hMixBit = CreateCompatibleBitmap(hdc, 819, 614);
    hHouseBit = (HBITMAP)LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_HOUSE));
    hTigerBit = (HBITMAP)LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_TIGER));
    
    // 提前创建静态DC并绑定位图
    memdc1 = CreateCompatibleDC(hdc);
    memdc2 = CreateCompatibleDC(hdc);
    SelectObject(memdc1, hHouseBit);
    SelectObject(memdc2, hTigerBit);
    
    ReleaseDC(hwnd, hdc);
    break;  

同时在WM_DESTROY中销毁这些静态DC,避免资源泄漏。

修正后的WM_PAINT代码示例

case WM_PAINT:
    hdc = BeginPaint(hwnd, &ps); 

    memdc = CreateCompatibleDC(hdc);
    oldMixBit = (HBITMAP)SelectObject(memdc, hMixBit);

    // 使用提前创建好的memdc1、memdc2
    BitBlt(memdc, 0, 0, 819, 614, memdc1, 0, 0, SRCCOPY);
    BitBlt(memdc, posX, 0, 67, 47, memdc2, 0, 0, SRCCOPY);
    BitBlt(hdc, 0, 0, 819, 614, memdc, 0, 0, SRCCOPY);

    SelectObject(memdc, oldMixBit);
    DeleteDC(memdc);
    EndPaint(hwnd, &ps);
    break;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 10:36:20