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
相关产品推荐
相关产品推荐

