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

Rust中如何将main的heventlog传递给service_main函数

Rust中传递上下文到Windows服务入口函数

在Rust中处理Windows服务的上下文传递,由于WinAPI的service_main函数签名固定,无法直接携带参数,我们可以通过线程安全的静态全局变量来实现,替代其他语言中的普通全局变量,同时保证Rust的内存安全。

解决方案步骤

  1. 定义线程安全的静态全局存储
    使用OnceLock(Rust 1.70+内置)来存储Arc<Mutex<HANDLE>>,确保全局变量只被初始化一次,且线程安全。

  2. 在main中初始化全局变量
    将创建好的heventlog存入全局存储,确保service_main执行前已完成初始化。

  3. 在service_main中获取并使用上下文
    从全局存储中取出heventlog,即可用于日志记录。

修改后的完整代码

use std::sync::{Arc, Mutex, OnceLock};
use windows::core::{w, PWSTR};
use windows::Win32::Foundation::HANDLE;
use windows::Win32::System::Services::{
    RegisterServiceCtrlHandlerW, SetServiceStatus, StartServiceCtrlDispatcherW,
    SERVICE_ACCEPT_STOP, SERVICE_CONTROL_STOP, SERVICE_RUNNING, SERVICE_START_PENDING,
    SERVICE_STATUS, SERVICE_STOPPED, SERVICE_TABLE_ENTRYW, SERVICE_WIN32_OWN_PROCESS,
};
use windows::Win32::System::EventLog::{DeregisterEventSource, ReportEventW};

// 全局存储EventLog句柄,线程安全且仅初始化一次
static EVENT_LOG_HANDLE: OnceLock<Arc<Mutex<HANDLE>>> = OnceLock::new();

unsafe extern "system" fn service_control_handler(dwcontrol: u32) {
    match dwcontrol {
        SERVICE_CONTROL_STOP => {
            // 停止服务时注销EventLog句柄
            if let Some(handle) = EVENT_LOG_HANDLE.get() {
                let handle = handle.lock().unwrap();
                DeregisterEventSource(*handle).unwrap();
            }
            return;
        }
        _ => {}
    }
}

unsafe extern "system" fn service_main(_dwnumservicesargs: u32, _lpserviceargvectors: *mut PWSTR) {
    // 从全局存储获取EventLog句柄
    let heventlog = EVENT_LOG_HANDLE.get().expect("EventLog handle not initialized");
    let mut lpservicestatus = SERVICE_STATUS {
        dwServiceType: SERVICE_WIN32_OWN_PROCESS,
        dwCurrentState: SERVICE_START_PENDING,
        dwControlsAccepted: SERVICE_ACCEPT_STOP,
        dwWin32ExitCode: 0,
        dwServiceSpecificExitCode: 0,
        dwCheckPoint: 0,
        dwWaitHint: 0,
    };

    let lpservicename = w!("Demo Service");
    let hservicestatus =
        RegisterServiceCtrlHandlerW(lpservicename, Some(service_control_handler)).unwrap();

    // 示例:记录服务启动日志
    let event_id = 1000u32;
    ReportEventW(
        *heventlog.lock().unwrap(),
        windows::Win32::System::EventLog::EVENTLOG_INFORMATION_TYPE,
        0,
        event_id,
        None,
        0,
        0,
        std::ptr::null(),
        std::ptr::null(),
    ).unwrap();

    lpservicestatus.dwCurrentState = SERVICE_RUNNING;
    SetServiceStatus(hservicestatus, &mut lpservicestatus).unwrap();
}

fn main() {
    use windows::Win32::System::EventLog::RegisterEventSourceW;
    let heventlog = unsafe { RegisterEventSourceW(None, w!("Demo Service")) }.unwrap();
    let heventlog: Arc<Mutex<HANDLE>> = Arc::new(Mutex::new(heventlog));
    
    // 将句柄存入全局存储
    EVENT_LOG_HANDLE.set(heventlog).expect("Failed to set global EventLog handle");

    let mut lp_service_name = "Demo Service\0".encode_utf16().collect::<Vec<u16>>();
    let lp_service_name = PWSTR::from_raw(lp_service_name.as_mut_ptr());

    let lpservicestarttable = [
        SERVICE_TABLE_ENTRYW {
            lpServiceName: lp_service_name,
            lpServiceProc: Some(service_main),
        },
        SERVICE_TABLE_ENTRYW {
            lpServiceName: PWSTR::null(),
            lpServiceProc: None,
        },
    ]
    .as_ptr();

    unsafe { StartServiceCtrlDispatcherW(lpservicestarttable) }.unwrap();
}

关键说明

  • OnceLock的作用:确保全局变量仅被初始化一次,避免重复赋值导致的资源泄漏或错误,同时提供线程安全的访问。
  • Arc<Mutex>:因为服务的控制处理函数(service_control_handler)和service_main可能在不同线程执行,Mutex保证句柄访问的互斥性,Arc允许多线程共享所有权。
  • 资源清理:在service_control_handler处理停止命令时,调用DeregisterEventSource释放句柄,避免资源泄漏。

内容的提问来源于stack exchange,提问作者xfvgk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 08:23:18