Win32 Layered Window透明窗口异常:红框无法显示问题求助
解决Win32分层透明窗口仅显示红框并支持穿透交互的问题
尝试使用Win32 API创建一个透明窗口,要求窗口中心显示红色方框,同时允许用户与窗口后方的对象交互。当前问题是屏幕上看不到红框;将
SetLayeredWindowAttributes(hwnd, 0, 0, winuser::LWA_ALPHA);中的bAlpha参数设为255时,红框可见但会出现黑色背景,不符合仅显示红框、背景全透明的需求。
问题原因
- 使用
LWA_ALPHA将窗口alpha值设为0时,整个窗口(包括绘制的红框)会完全透明,因此无法看到红框。 - alpha值设为255时窗口完全不透明,窗口类使用
NULL_BRUSH会导致系统用黑色填充背景,出现不符合需求的黑底。 WS_EX_TRANSPARENT已经能实现鼠标穿透,但透明方式选择错误导致显示异常。
解决方案
采用颜色键透明方案:指定一种颜色作为透明色,窗口背景使用该颜色,绘制红框时使用其他颜色,系统会自动将透明色区域转为透明,同时保留红框显示,且不影响鼠标穿透。
修改后的完整代码
extern crate winapi; use winapi::{ shared::{minwindef, windef}, um::{libloaderapi, wingdi, winuser, winuser::PAINTSTRUCT}, }; use std::ptr; // 定义透明色(这里选择黑色,可根据需求修改) const TRANSPARENT_COLOR: wingdi::COLORREF = wingdi::RGB(0, 0, 0); unsafe extern "system" fn wnd_proc( hwnd: winapi::shared::windef::HWND, msg: minwindef::UINT, wparam: minwindef::WPARAM, lparam: minwindef::LPARAM, ) -> minwindef::LRESULT { match msg { winuser::WM_PAINT => { let mut ps: PAINTSTRUCT = std::mem::zeroed(); let hdc = winuser::BeginPaint(hwnd, &mut ps); // 获取窗口客户端区域大小 let mut client_rect = windef::RECT::default(); winuser::GetClientRect(hwnd, &mut client_rect); // 先填充整个窗口为透明色 let transparent_brush = wingdi::CreateSolidBrush(TRANSPARENT_COLOR); winuser::FillRect(hdc, &client_rect, transparent_brush); wingdi::DeleteObject(transparent_brush as *mut _); // 计算居中的红框矩形 let rect_width = 600; let rect_height = 400; let rect = windef::RECT { left: (client_rect.right - rect_width) / 2, top: (client_rect.bottom - rect_height) / 2, right: (client_rect.right + rect_width) / 2, bottom: (client_rect.bottom + rect_height) / 2, }; // 创建红色画刷并绘制红框区域 let red_brush = wingdi::CreateSolidBrush(wingdi::RGB(255, 0, 0)); winuser::FillRect(hdc, &rect, red_brush); wingdi::DeleteObject(red_brush as *mut _); winuser::EndPaint(hwnd, &ps); 0 } winuser::WM_ERASEBKGND => { // 返回非0阻止系统默认绘制背景,避免闪烁 1 } winuser::WM_DESTROY => { winuser::PostQuitMessage(0); 0 } _ => winuser::DefWindowProcA(hwnd, msg, wparam, lparam), } } fn main() { unsafe { let h_instance = libloaderapi::GetModuleHandleA(ptr::null()); let class_name = "GPUWindow"; let window_name = "GPU Window"; // 创建透明色背景刷 let transparent_brush = wingdi::CreateSolidBrush(TRANSPARENT_COLOR); 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: ptr::null_mut(), hCursor: winuser::LoadCursorA(ptr::null_mut(), winuser::IDC_ARROW as *const i8), hbrBackground: transparent_brush as *mut _, lpszMenuName: ptr::null_mut(), }; winuser::RegisterClassA(&wnd_class); let hwnd = winuser::CreateWindowExA( winuser::WS_EX_LAYERED | winuser::WS_EX_TRANSPARENT, class_name.as_ptr() as *const i8, window_name.as_ptr() as *const i8, winuser::WS_POPUP, 0, 0, winuser::GetSystemMetrics(winuser::SM_CXSCREEN), winuser::GetSystemMetrics(winuser::SM_CYSCREEN), ptr::null_mut(), ptr::null_mut(), h_instance, ptr::null_mut(), ); // 设置颜色键透明:将TRANSPARENT_COLOR对应的区域设为透明 winuser::SetLayeredWindowAttributes(hwnd, TRANSPARENT_COLOR, 0, winuser::LWA_COLORKEY); winuser::ShowWindow(hwnd, winuser::SW_SHOW); winuser::UpdateWindow(hwnd); let mut msg: winuser::MSG = std::mem::zeroed(); while winuser::GetMessageA(&mut msg, hwnd, 0, 0) > 0 { winuser::TranslateMessage(&msg); winuser::DispatchMessageA(&msg); } // 清理背景刷资源 wingdi::DeleteObject(transparent_brush as *mut _); } }
关键修改说明
- 颜色键设置:定义
TRANSPARENT_COLOR为黑色,通过SetLayeredWindowAttributes的LWA_COLORKEY标志将该颜色区域设为透明。 - 背景处理:窗口类的背景刷使用透明色,同时处理
WM_ERASEBKGND消息阻止系统默认绘制,避免背景闪烁。 - 绘制逻辑:先填充整个窗口为透明色,再绘制居中的红框,确保只有红框区域可见,其余部分透明。
- 资源清理:手动创建的画刷在使用后删除,避免内存泄漏。
内容的提问来源于stack exchange,提问作者asdfasdf
相关产品推荐
相关产品推荐

