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

Windows GDI透明窗口旧绘制元素残留问题求助

问题根源分析

旧矩形残留的核心原因有三个:

  • 窗口使用WS_EX_TRANSPARENT扩展样式,该样式会跳过窗口背景擦除步骤,且禁止接收WM_ERASEBKGND消息,导致旧绘制内容无法被系统自动清除。
  • 分层窗口设置的颜色键为黑色(0),但绘制的红色矩形不属于透明色范围,旧红色像素不会被自动透明化,因此持续残留。
  • 重绘时仅绘制新矩形,未主动覆盖旧区域内容,叠加透明属性的影响,旧像素无法被清理。
解决方案

1. 调整窗口扩展样式

移除WS_EX_TRANSPARENT,改用WM_NCHITTEST消息实现鼠标穿透,避免影响窗口自身重绘机制:

// 修改CreateWindowExA的扩展样式参数
let hwnd = winuser::CreateWindowExA(
    winuser::WS_EX_LAYERED | winuser::WS_EX_TOPMOST, // 移除WS_EX_TRANSPARENT
    class_name.as_ptr() as *const i8,
    window_name.as_ptr() as *const i8,
    winuser::WS_POPUP,
    0, 0, 1000, 1000,
    std::ptr::null_mut(), std::ptr::null_mut(), h_instance, std::ptr::null_mut(),
);

同时在窗口过程中添加WM_NCHITTEST处理:

winuser::WM_NCHITTEST => {
    // 让窗口忽略鼠标事件,实现穿透
    return winuser::HTTRANSPARENT as winapi::shared::minwindef::LRESULT;
},

2. 重绘时主动清除背景

在WM_PAINT中,先用颜色键对应的黑色填充客户区,覆盖旧内容后再绘制新矩形:

winuser::WM_PAINT => {
    let mut ps: winuser::PAINTSTRUCT = std::mem::zeroed();
    let hdc = winuser::BeginPaint(hwnd, &mut ps);
    
    // 填充背景为黑色(颜色键对应色),清除旧内容
    let black_brush = winapi::um::wingdi::CreateSolidBrush(winapi::um::wingdi::RGB(0, 0, 0));
    winapi::um::wingdi::FillRect(hdc, &ps.rcPaint, black_brush);
    winapi::um::wingdi::DeleteObject(black_brush as *mut _);
    
    let boxes_unwrapped = boxes.lock().unwrap();
    println!("drawing {}", boxes_unwrapped.len());

    if boxes_unwrapped.len() > 0 {
        for rect in boxes_unwrapped.iter() {
            let red_brush = winapi::um::wingdi::CreateSolidBrush(winapi::um::wingdi::RGB(255, 0, 0));
            winuser::FrameRect(hdc, rect as *const _, red_brush);
            winapi::um::wingdi::DeleteObject(red_brush as *mut _);
        }
    }

    drop(boxes_unwrapped);
    winuser::EndPaint(hwnd, &ps);
},

3. 确保重绘区域正确触发

你的后台线程已经在修改boxes后调用InvalidateRect,且最后一个参数设为1(TRUE),该设置正确,无需修改,它会确保系统标记整个窗口为需要重绘的区域。

修改后的核心代码片段

窗口过程(新增WM_NCHITTEST及背景填充)

unsafe extern "system" fn wnd_proc(
    hwnd: winapi::shared::windef::HWND,
    msg: winapi::shared::minwindef::UINT,
    wparam: winapi::shared::minwindef::WPARAM,
    lparam: winapi::shared::minwindef::LPARAM,
) -> winapi::shared::minwindef::LRESULT {
    match msg {
        winuser::WM_PAINT => {
            let mut ps: winuser::PAINTSTRUCT = std::mem::zeroed();
            let hdc = winuser::BeginPaint(hwnd, &mut ps);
            
            // 填充背景为黑色(颜色键对应色)
            let black_brush = winapi::um::wingdi::CreateSolidBrush(winapi::um::wingdi::RGB(0, 0, 0));
            winapi::um::wingdi::FillRect(hdc, &ps.rcPaint, black_brush);
            winapi::um::wingdi::DeleteObject(black_brush as *mut _);
            
            let boxes_unwrapped = boxes.lock().unwrap();
            println!("drawing {}", boxes_unwrapped.len());

            if boxes_unwrapped.len() > 0 {
                for rect in boxes_unwrapped.iter() {
                    let red_brush = winapi::um::wingdi::CreateSolidBrush(winapi::um::wingdi::RGB(255, 0, 0));
                    winuser::FrameRect(hdc, rect as *const _, red_brush);
                    winapi::um::wingdi::DeleteObject(red_brush as *mut _);
                }
            }

            drop(boxes_unwrapped);
            winuser::EndPaint(hwnd, &ps);
        },
        winuser::WM_NCHITTEST => {
            return winuser::HTTRANSPARENT as winapi::shared::minwindef::LRESULT;
        },
        winuser::WM_DESTROY => {
            winuser::PostQuitMessage(0);
        },
        _ => {
            return winuser::DefWindowProcA(hwnd, msg, wparam, lparam);
        },
    }
    return 0;
}

初始化函数(移除WS_EX_TRANSPARENT)

fn initialize() -> winapi::shared::windef::HWND {
    unsafe {
        let h_instance = winapi::um::libloaderapi::GetModuleHandleA(std::ptr::null_mut());

        let class_name = "GPUWindow";
        let window_name = "GPU Window";

        let wnd_class = winuser::WNDCLASSA {
            style: winuser::CS_HREDRAW | winuser::CS_VREDRAW,
            lpfnWndProc: Some(wnd_proc),
            hInstance: h_instance,
            lpszClassName: class_name.as_ptr() as *const i8,
            cbClsExtra: 0,
            cbWndExtra: 0,
            hIcon: std::ptr::null_mut(),
            hCursor: winuser::LoadCursorA(std::ptr::null_mut(), winuser::IDC_ARROW as *const i8),
            hbrBackground: winapi::um::wingdi::GetStockObject(winapi::um::wingdi::NULL_BRUSH as i32) as *mut _,
            lpszMenuName: std::ptr::null_mut(),
        };

        winuser::RegisterClassA(&wnd_class);

        let hwnd = winuser::CreateWindowExA(
            winuser::WS_EX_LAYERED | winuser::WS_EX_TOPMOST,
            class_name.as_ptr() as *const i8,
            window_name.as_ptr() as *const i8,
            winuser::WS_POPUP,
            0,
            0,
            1000,
            1000,
            std::ptr::null_mut(),
            std::ptr::null_mut(),
            h_instance,
            std::ptr::null_mut(),
        );

        winuser::SetLayeredWindowAttributes(hwnd, 0, 255, winuser::LWA_COLORKEY);

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

        hwnd
    }
}

修改后,每次重绘都会先清除旧内容,再绘制新矩形,旧矩形残留问题即可解决。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 06:55:18