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

为何无法用ValidateRect()替代BeginPaint()?

WM_CREATE绘制后WM_PAINT仅调用ValidateRect导致窗口空白的问题

我理解BeginPaint和EndPaint可用于绘制屏幕并验证绘制的矩形区域,还能避免WM_PAINT消息刷屏(因为窗口不再有无效区域)。基于此,我认为理论上若在WM_CREATE中用BitBlt()绘制窗口,再用ValidateRect验证整个客户区,代码应能正常运行。但实际尝试后却失败了——将WM_PAINT中的代码替换为ValidateRect(hwnd,NULL);break;后,整个窗口不再显示。请问我哪里理解有误或代码存在问题?以下是我使用的代码,IDB_BITMAP1可替换为任意合适的位图:

#include <windows.h>
#include "resource.h"

HBITMAP hBitMap = NULL;

LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CREATE:
    {
        BITMAP infoBM;
        hBitMap = LoadBitmap(GetModuleHandle(NULL), IDB_BITMAP1);
        if (hBitMap == NULL)
        {
            MessageBoxA(NULL, "COULD NOT LOAD CAT BITMAP", "ERROR", MB_OK);
        }
        HDC winDC = GetDC(hwnd);
        HDC hMem = CreateCompatibleDC(winDC);
        GetObject(hBitMap, sizeof(infoBM), &infoBM);
        HBITMAP hMemOld = SelectObject(hMem, hBitMap);
        BitBlt(winDC, 0, 0, infoBM.bmWidth, infoBM.bmHeight, hMem, 0, 0, SRCCOPY);
        SelectObject(hMem, hMemOld);
        DeleteDC(hMem);
        ReleaseDC(hwnd,winDC);
        break;
    }
    case WM_PAINT: //<<<--------- 我把这里的代码替换成ValidateRect(hwnd,NULL);break;后窗口就不显示了,为什么?
    {
        BITMAP infoBM;
        PAINTSTRUCT ps;
        HDC winDC = BeginPaint(hwnd, &ps);
        GetObject(hBitMap, sizeof(infoBM), &infoBM);
        HDC hMem = CreateCompatibleDC(winDC);
        HBITMAP hOldMem = SelectObject(hMem, hBitMap);
        BitBlt(winDC, 0, 0, infoBM.bmWidth, infoBM.bmHeight, hMem, 0, 0, SRCCOPY);
        SelectObject(hMem, hOldMem);
        DeleteObject(hMem);
        EndPaint(hwnd, &ps);
        break;
    }
    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:return DefWindowProcA(hwnd, msg, wParam, lParam);
    }
return 0;
}

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc = { sizeof(WNDCLASSEX),0,WindowProc,0,0,hInstance,NULL,LoadCursor(NULL,IDC_ARROW),(HBRUSH)(COLOR_WINDOW+1),NULL,"WindowClass",LoadIcon(NULL,IDI_APPLICATION)};
    if (!RegisterClassEx(&wc))
    {
        MessageBoxA(NULL, "WINDOW REGISTRATION FAILED", "ERROR", MB_OK);
    }

    HWND hwnd = CreateWindowEx(WS_EX_LAYERED, "WindowClass", "Title", WS_POPUP, 200, 200, 300, 300, NULL, NULL, hInstance, NULL);
    if (!hwnd)
    {
        MessageBoxA(NULL, "WINDOW CREATION FAILED", "ERROR", MB_OK);
    }

    ShowWindow(hwnd, SW_SHOW);
    UpdateWindow(hwnd);

    if (!SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 255, LWA_ALPHA))
    {
        MessageBoxA(NULL, "WINDOW LAYERED ATTRIBUTES FAILED", "ERROR", MB_OK);
    }

    MSG msg;
    while (GetMessage(&msg, hwnd, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

问题原因分析

  1. WM_CREATE阶段的绘制会被覆盖
    WM_CREATE是窗口创建初期的消息,此时窗口还未执行ShowWindow和UpdateWindow,你用GetDC绘制的内容会被后续窗口显示流程中的系统刷新操作清空,根本无法保留到窗口显示的时候。

  2. 分层窗口的绘制机制不兼容普通GDI操作
    你创建的是带WS_EX_LAYERED扩展风格的分层窗口,这类窗口的图像合成由系统单独处理,普通的GetDC+BitBlt绘制不会被系统正确捕获并渲染到屏幕上,必须使用分层窗口专用的绘制接口(比如UpdateLayeredWindow),或者在WM_PAINT中配合BeginPaint/EndPaint完成绘制。

  3. WM_PAINT的处理不符合系统要求
    WM_PAINT消息必须配套调用BeginPaint和EndPaint,不能仅用ValidateRect。BeginPaint不仅会标记无效区域为有效,还会初始化绘制上下文的状态,对于分层窗口来说,缺少这对函数的正确调用会导致系统无法完成窗口图像的合成,最终显示空白。


修正方案

方案一:去掉分层窗口风格(如果不需要分层效果)

修改CreateWindowEx的扩展风格为0,然后将所有绘制逻辑保留在WM_PAINT中(这是标准窗口的正确绘制方式):

HWND hwnd = CreateWindowEx(0, "WindowClass", "Title", WS_POPUP, 200, 200, 300, 300, NULL, NULL, hInstance, NULL);

方案二:使用分层窗口的正确绘制方式

如果需要保留WS_EX_LAYERED,可以改用UpdateLayeredWindow来设置窗口图像,示例如下(在WM_CREATE中完成绘制并更新分层窗口):

case WM_CREATE:
{
    BITMAP infoBM;
    hBitMap = LoadBitmap(GetModuleHandle(NULL), IDB_BITMAP1);
    if (hBitMap == NULL)
    {
        MessageBoxA(NULL, "COULD NOT LOAD CAT BITMAP", "ERROR", MB_OK);
    }
    GetObject(hBitMap, sizeof(infoBM), &infoBM);
    
    HDC winDC = GetDC(hwnd);
    HDC hMem = CreateCompatibleDC(winDC);
    HBITMAP hOldMem = SelectObject(hMem, hBitMap);
    
    POINT ptSrc = {0,0};
    POINT ptDst = {0,0};
    SIZE szWnd = {infoBM.bmWidth, infoBM.bmHeight};
    BLENDFUNCTION blend = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
    
    UpdateLayeredWindow(hwnd, NULL, NULL, &szWnd, hMem, &ptSrc, 0, &blend, ULW_ALPHA);
    
    SelectObject(hMem, hOldMem);
    DeleteDC(hMem);
    ReleaseDC(hwnd, winDC);
    break;
}
case WM_PAINT:
{
    // 分层窗口无需处理WM_PAINT,直接返回即可
    ValidateRect(hwnd, NULL);
    break;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 00:55:13