Rust Win32窗口PeekMessage无法捕获WM_QUIT导致主循环无法退出
Rust Win32窗口主循环无法捕获WM_QUIT消息问题
问题现象
使用Rust调用Win32 API编写窗口程序时,点击关闭按钮后窗口销毁,但主循环始终无法检测到WM_QUIT消息,程序持续运行无法退出。预期行为是调用PostQuitMessage(0)后,主循环捕获WM_QUIT并终止。
错误原因
- PeekMessageW参数过滤了WM_QUIT:
PeekMessageW(&mut msg, self.hwnd, 0, 0, PM_REMOVE)中第二个参数指定了当前窗口的句柄self.hwnd,而WM_QUIT是线程级消息,不属于任何窗口,因此会被PeekMessage过滤,导致主循环永远接收不到该消息。 - 窗口过程中重复投递退出消息:
WM_CLOSE分支中手动调用DestroyWindow(hwnd)并投递PostQuitMessage(0),而默认的DefWindowProcW处理WM_CLOSE时会自动调用DestroyWindow;同时WM_DESTROY分支又再次投递PostQuitMessage(0),虽不会直接导致循环无法退出,但属于冗余操作。
修复方案
1. 修改主循环的PeekMessage调用
将PeekMessageW的窗口句柄参数改为null_mut(),这样会接收当前线程的所有消息,包括WM_QUIT:
if unsafe { PeekMessageW(&mut msg, null_mut(), 0, 0, PM_REMOVE) } != 0 {
2. 修正窗口过程函数(window_proc)
移除WM_CLOSE分支中的手动操作,让系统默认处理WM_CLOSE(自动调用DestroyWindow),仅在WM_DESTROY中调用PostQuitMessage(0):
pub extern "system" fn window_proc(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> LRESULT { match msg { WM_DESTROY => { unsafe { PostQuitMessage(0) }; 0 } _ => return unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) } } }
完整修正后的win64.rs代码
#![cfg(windows)] use std::error::Error; use std::ptr::null_mut; use std::mem::size_of; use winapi::shared::minwindef::*; use winapi::shared::windef::*; use winapi::um::libloaderapi::{ GetModuleHandleW }; use winapi::um::wingdi::*; use winapi::um::winuser::*; fn to_wstring(s: &str) -> Vec<u16> { use std::os::windows::ffi::OsStrExt; std::ffi::OsStr::new(s).encode_wide().chain(std::iter::once(0)).collect() } pub extern "system" fn window_proc(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> LRESULT { match msg { WM_DESTROY => { unsafe { PostQuitMessage(0) }; 0 } _ => return unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) } } } pub struct Window { h_instance: HINSTANCE, hwnd: HWND, rect: RECT, } impl Window { pub fn new(title: &str, width: u32, height: u32) -> Result<Window, Box<(dyn Error + 'static)>> { let title = to_wstring(title); let class_name = to_wstring("_class64"); let mut wnd = Window { h_instance: unsafe { GetModuleHandleW(null_mut()) }, hwnd: null_mut(), rect: RECT { left: 0, top: 0, right: width as i32, bottom: height as i32, } }; if wnd.h_instance == null_mut() { let err_str = "failed to obtain window instance"; unsafe { MessageBoxW(null_mut(), to_wstring(err_str).as_ptr(), to_wstring("Error").as_ptr(), MB_ICONERROR | MB_OK); } return Err(err_str.into()); } let wnd_class_ex = WNDCLASSEXW { cbClsExtra: 0, cbSize: size_of::<WNDCLASSEXW>() as u32, cbWndExtra: 0, hbrBackground: unsafe { GetStockObject(BLACK_BRUSH as i32) as HBRUSH}, hCursor: unsafe { LoadCursorW(null_mut(), IDC_ARROW) }, hIcon: unsafe { LoadIconW(null_mut(), IDI_APPLICATION) }, hIconSm: unsafe { LoadIconW(null_mut(), IDI_APPLICATION) }, hInstance: wnd.h_instance, lpfnWndProc: Some(window_proc), lpszClassName: class_name.as_ptr(), lpszMenuName: null_mut(), style: CS_OWNDC | CS_HREDRAW | CS_VREDRAW, }; if unsafe { RegisterClassExW(&wnd_class_ex) } == 0 { let err_str = "failed to register window"; unsafe { MessageBoxW(null_mut(), to_wstring(err_str).as_ptr(), to_wstring("Error").as_ptr(), MB_ICONERROR | MB_OK); } return Err(err_str.into()); } wnd.hwnd = unsafe { CreateWindowExW( 0, class_name.as_ptr(), title.as_ptr(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, wnd.rect.right, wnd.rect.bottom, null_mut(), null_mut(), wnd.h_instance, null_mut()) }; if wnd.hwnd == null_mut() { let err_str = "failed to create window"; unsafe { MessageBoxW(null_mut(), to_wstring(err_str).as_ptr(), to_wstring("Error").as_ptr(), MB_ICONERROR | MB_OK); } return Err(err_str.into()); } unsafe { ShowWindow(wnd.hwnd, SW_SHOW); } unsafe { UpdateWindow(wnd.hwnd); } Ok(wnd) } pub fn run(&mut self) -> isize { let mut msg = MSG { hwnd: null_mut(), lParam: 0, message: 0, pt: POINT { x: 0, y: 0 }, time: 0, wParam: 0, }; loop { if unsafe { PeekMessageW(&mut msg, null_mut(), 0, 0, PM_REMOVE) } != 0 { if msg.message == WM_QUIT { break; } unsafe { TranslateMessage(&msg); } unsafe { DispatchMessageW(&msg); } } else { // TODO: Add stuff here } } 0 } }
内容的提问来源于stack exchange,提问作者fluffy-soft-dev
相关产品推荐
相关产品推荐

