使用Rust Windows Crate和EvtSubscribe监听Windows事件的资源清理问题
问题根源
你的代码里EvtClose写在死循环后面,根本执行不到。用CTRL+C强制退出时,订阅句柄没被关闭,Windows系统里残留了无效资源,导致再次运行时触发通道路径无效或找不到的错误。
解决办法
要在程序被强制终止前清理句柄,得捕获CTRL+C信号,在退出前调用EvtClose释放资源。修改后的代码如下:
use std::ffi::{CString, OsString}; use std::io::Error; use std::thread; use std::time::Duration; use std::sync::Arc; use windows::core::PCWSTR; use windows::Win32::System::EventLog::*; use signal_hook::{consts::SIGINT, iterator::Signals}; fn main() { let channel = to_wchar("Application"); let query = to_wchar("*"); // 打开事件日志订阅 let subscription = unsafe { EvtSubscribe( None, None, channel, query, None, None, Some(event_callback), EvtSubscribeStartAtOldestRecord.0, ) }; let error = Error::last_os_error(); println!("Seen: {:?}", error); let subscription = match subscription { Ok(handle) => Arc::new(handle), Err(sub_error) => { println!( "Failed to subscribe to event log: {:?} :: {:?}", error, sub_error ); return; } }; // 捕获CTRL+C信号 let mut signals = Signals::new(&[SIGINT]).unwrap(); let sub_clone = Arc::clone(&subscription); // 启动线程监听信号,收到信号就清理句柄并退出 thread::spawn(move || { for _ in signals.forever() { println!("Received CTRL+C, cleaning up..."); unsafe { EvtClose(*sub_clone); } std::process::exit(0); } }); // 保持程序运行 loop { thread::sleep(Duration::from_millis(10)); } } unsafe extern "system" fn event_callback( _: EVT_SUBSCRIBE_NOTIFY_ACTION, _: *const std::ffi::c_void, event_handle: EVT_HANDLE, ) -> u32 { println!("CALLBACK!"); // 回调里的event_handle也要关闭,避免内存泄漏 EvtClose(event_handle); 0 } fn to_wchar(str: &str) -> PCWSTR { let mut raw_mut_str = str.encode_utf16().chain(Some(0)).collect::<Vec<u16>>(); let str_ptr = raw_mut_str.as_mut_slice().as_mut_ptr(); PCWSTR::from_raw(str_ptr) }
重要修改点
- 用
signal-hook库捕获CTRL+C信号,在信号处理线程中关闭订阅句柄再退出 - 用
Arc共享句柄,让信号线程能访问到订阅句柄 - 补充了回调函数中
event_handle的关闭,避免额外资源泄漏 - 需要在
Cargo.toml里加依赖:signal-hook = "0.3"
替代方案(不用第三方库)
如果不想用第三方库,可以调用Windows API的SetConsoleCtrlHandler来处理控制台关闭事件,不过代码会更繁琐,核心逻辑还是在收到终止信号时调用EvtClose。
内容的提问来源于stack exchange,提问作者user1664202
相关产品推荐
相关产品推荐

