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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 17:07:24