Rust中如何将main的heventlog传递给service_main函数
Rust中传递上下文到Windows服务入口函数
在Rust中处理Windows服务的上下文传递,由于WinAPI的service_main函数签名固定,无法直接携带参数,我们可以通过线程安全的静态全局变量来实现,替代其他语言中的普通全局变量,同时保证Rust的内存安全。
解决方案步骤
定义线程安全的静态全局存储
使用OnceLock(Rust 1.70+内置)来存储Arc<Mutex<HANDLE>>,确保全局变量只被初始化一次,且线程安全。在main中初始化全局变量
将创建好的heventlog存入全局存储,确保service_main执行前已完成初始化。在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
相关产品推荐
相关产品推荐

