首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Windows GDI 雙緩衝繪圖白屏問題:核心原因與修復實戰

     編輯:程式設計綜合問答

問題現象

VC++ 窗口程序嘗試用雙緩衝消除閃爍,卻始終顯示白屏。核心代碼片段如下:

關鍵錯誤代碼段

case WM_CREATE:
    hCloudBMP = (HBITMAP)LoadImage(NULL, "1.bmp", IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
    cloudDC = CreateCompatibleDC(hdc);          // ❌ hdc 此時未初始化
    SelectObject(cloudDC, hCloudBMP);
    ReleaseDC(hWnd, hdc);                       // ❌ 過早釋放 hdc

    hBgBMP = (HBITMAP)LoadImage(NULL, "bg.bmp", IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
    bgDC = CreateCompatibleDC(hdc);             // ❌ hdc 已被 ReleaseDC,句柄無效
    SelectObject(bgDC, hBgBMP);
    ReleaseDC(hWnd, hdc);

    hMemDC = CreateCompatibleDC(hdc);           // ❌ 致命:hdc 已失效,創建失敗返回 NULL
    hTemp = CreateCompatibleBitmap(hdc, 1280, 720); // ❌ 同理失敗
    SelectObject(hMemDC, hTemp);
    ReleaseDC(hWnd, hdc);
    SetTimer(hWnd, 100, 100, NULL);
    break;

根因分析

  1. WM_CREATE 階段 hdc 尚未獲取(需在 WM_PAINT 中 BeginPaint 獲取,或用 GetDC)。
  2. ReleaseDC(hWnd, hdc) 過早釋放導致後續 CreateCompatibleDC(hdc) 參數無效,API 調用失敗返回 NULL。
  3. 後續 BitBlt 操作無效 DC,自然白屏。

修正方案

1. 正確獲取與釋放 DC 的時機

// 全局/靜態變量保存位圖與兼容 DC,不保存 hdc
static HBITMAP hCloudBMP, hBgBMP, hTemp;
static HDC cloudDC, bgDC, hMemDC;
static BITMAP bm, bgBm;

// WM_CREATE:只加載資源、創建兼容 DC(用屏幕 DC 或稍後在 WM_PAINT 獲取)
case WM_CREATE:
    hCloudBMP = (HBITMAP)LoadImage(NULL, "1.bmp", IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
    hBgBMP    = (HBITMAP)LoadImage(NULL, "bg.bmp", IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);

    // 用屏幕 DC 創建兼容 DC,不依賴窗口 hdc
    HDC hScreenDC = GetDC(NULL);
    cloudDC = CreateCompatibleDC(hScreenDC);
    bgDC    = CreateCompatibleDC(hScreenDC);
    hMemDC  = CreateCompatibleDC(hScreenDC);
    ReleaseDC(NULL, hScreenDC);

    SelectObject(cloudDC, hCloudBMP);
    SelectObject(bgDC, hBgBMP);
    GetObject(hCloudBMP, sizeof(BITMAP), &bm);
    GetObject(hBgBMP, sizeof(BITMAP), &bgBm);

    // 創建與屏幕兼容的位圖作為後備緩衝
    hTemp = CreateCompatibleBitmap(hScreenDC, 1280, 720); // 或用 GetSystemMetrics 獲取分辨率
    SelectObject(hMemDC, hTemp);

    SetTimer(hWnd, 100, 100, NULL);
    break;

2. WM_PAINT 中正確雙緩衝流程

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

    // 1. 先將背景繪到內存 DC
    BitBlt(hMemDC, 0, 0, bgBm.bmWidth, bgBm.bmHeight, bgDC, 0, 0, SRCCOPY);

    // 2. 再繪制移動圖層
    BitBlt(hMemDC, x, y, bm.bmWidth, bm.bmHeight, cloudDC, 0, 0, SRCCOPY);

    // 3. 一次性將內存 DC 複制到窗口 DC
    BitBlt(hdc, 0, 0, 1280, 720, hMemDC, 0, 0, SRCCOPY);

    EndPaint(hWnd, &ps);
    break;

3. 資源釋放 (WM_DESTROY)

case WM_DESTROY:
    DeleteDC(cloudDC); DeleteObject(hCloudBMP);
    DeleteDC(bgDC);    DeleteObject(hBgBMP);
    DeleteDC(hMemDC);  DeleteObject(hTemp);
    KillTimer(hWnd, 100);
    PostQuitMessage(0);
    break;

調試鐵律

每個 GDI API 必須檢查返回值,失敗時調用 GetLastError() 定位錯誤碼。若作者在 CreateCompatibleDC 後檢查 NULL 並打印錯誤,兩三天的折騰可縮減至幾分鐘。

關鍵點總結表

錯誤做法正確做法
WM_CREATE 用未初始化 hdc用 GetDC(NULL) 獲取屏幕 DC 創建兼容 DC
過早 ReleaseDC 導致後續 API 失敗兼容 DC/位圖創建完成後無需窗口 DC,生命周期獨立
不檢查 API 返回值所有 GDI 調用必判 NULL/0,配合 GetLastError()
BitBlt 目標 DC 錯誤內存 DC → 窗口 DC,單向流向清晰

按此修正,雙緩衝即可正常工作,告別白屏與閃爍。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved