• Rust编写Windows服务


    Rust编写Windows服务

    编写Windows服务可选语言很多, 其中C#最简单。本着练手Rust语言,尝试用Rust编写一个服务。

    一:Windows服务程序大致原理

    参考官网C/C++创建服务流程
    https://learn.microsoft.com/zh-cn/windows/win32/services/service-program-tasks

    • 编写服务程序的main函数
      1. main函数中,填充数据结构 DispatchTable: [[服务名,ServiceMain函数], [NULL, NULL]]
      2. 调用StartServiceCtrlDispatcher( DispatchTable )
    int __cdecl _tmain(int argc, TCHAR *argv[])
    { 
        SERVICE_TABLE_ENTRY DispatchTable[] = 
        { 
            { SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain }, 
            { NULL, NULL } 
        }; 
        if (!StartServiceCtrlDispatcher( DispatchTable )) 
        { 
            SvcReportEvent(TEXT("StartServiceCtrlDispatcher")); 
        } 
    } 
    
    
    • 编写 ServiceMain 函数
      1. 注册控制处理函数 RegisterServiceCtrlHandler(SvcCtrlHandler )
      2. 设置服务状态,SERVICE_START_PENDING
      3. 做一些预备工作,比如初始化日志/注册事件等
      4. 设置服务状态,SERVICE_START_RUNNING
      5. 开始loop处理我们自己的代码(loop循环中可以接受3中注册的事件,当通知停止时退出循环)
    • 编写控件处理程序函数
      1. 接受事件管理器发送的消息并处理,比如收到SERVICE_CONTROL_STOP时,使用3中注册的事件句柄发送停止事件

    二:Rust中编写windows服务

    借用第三方库 windows-service = "0.7.0"
    https://crates.io/crates/windows-service
    参考windows-service中ping-service示例提取了一个模板,只有替换编写两处/* */代码

    use std::{
        ffi::OsString,
        sync::mpsc,
        time::Duration,
    };
    use windows_service::{
        define_windows_service,
        service::{
            ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus,
            ServiceType,
        },
        service_control_handler::{self, ServiceControlHandlerResult},
        service_dispatcher,
    };
    
    static SERVICE_NAME: &str = "Rust Demo Service";
    
    fn main() -> Result<(), windows_service::Error> {
        service_dispatcher::start(SERVICE_NAME, ffi_service_main)?;
        Ok(())
    }
    
    
    define_windows_service!(ffi_service_main, my_service_main);
    
    fn my_service_main(arguments: Vec<OsString>) {
        let _ = arguments;
            
        /*
        	这里服务还没开始, 可以填写初始化日志文件等操作
        */
        
        let (shutdown_tx, shutdown_rx) = mpsc::channel();
    
        // 对应SvcCtrlHandler
        let _event_handler = move |control_event| -> ServiceControlHandlerResult {
            match control_event {
                ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
    
                // 处理停止事件
                ServiceControl::Stop => {
                    shutdown_tx.send(()).unwrap();
                    ServiceControlHandlerResult::NoError
                }
    
                // 其他用户事件都当作停止事件处理
                ServiceControl::UserEvent(code) => {
                    if code.to_raw() == 130 {
                        shutdown_tx.send(()).unwrap();
                    }
                    ServiceControlHandlerResult::NoError
                }
    
                _ => ServiceControlHandlerResult::NotImplemented,
            }
        };
    
        let status_handle = service_control_handler::register(SERVICE_NAME, _event_handler);
        let status_handle = status_handle.unwrap();
        let _ = status_handle.set_service_status(ServiceStatus {
            service_type: ServiceType::OWN_PROCESS,
            current_state: ServiceState::Running,
            controls_accepted: ServiceControlAccept::STOP,
            exit_code: ServiceExitCode::Win32(0),
            checkpoint: 0,
            wait_hint: Duration::default(),
            process_id: None,
        });
    
        loop {
        	/*
    			这里写自己的代码逻辑,下面时处理一次循环后睡眠5秒,若是接受到停止等消息退出循环
    		*/
            match shutdown_rx.recv_timeout(Duration::from_secs(5)) {
                Ok(_) | Err(mpsc::RecvTimeoutError::Disconnected) => break,
                Err(mpsc::RecvTimeoutError::Timeout) => (),
            }
        }
    
        let _ = status_handle.set_service_status(ServiceStatus {
            service_type: ServiceType::OWN_PROCESS,
            current_state: ServiceState::Stopped,
            controls_accepted: ServiceControlAccept::empty(),
            exit_code: ServiceExitCode::Win32(0),
            checkpoint: 0,
            wait_hint: Duration::default(),
            process_id: None,
        });
    }
    
    

    三:具体实例

    笔记本策略经常恢复到合上盖子睡眠功能,写个小服务定时设置合上盖子不做任何操作
    逻辑比较简单,定时调用WinAPI函数CallNtPowerInformation获取配置信息,不符合当前设置执行修改
    完整如下

    use std::{
        ffi::{c_void, OsString},
        sync::mpsc,
        time::Duration,
    };
    use windows::Win32::{Foundation::STATUS_SUCCESS, System::Power::{CallNtPowerInformation, POWER_INFORMATION_LEVEL, SYSTEM_POWER_POLICY}};
    use windows_service::{
        define_windows_service,
        service::{
            ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus,
            ServiceType,
        },
        service_control_handler::{self, ServiceControlHandlerResult},
        service_dispatcher,
    };
    
    static SERVICE_NAME: &str = "Power Lid Service";
    
    fn main() -> Result<(), windows_service::Error> {
        service_dispatcher::start(SERVICE_NAME, ffi_service_main)?;
        Ok(())
    }
    
    
    define_windows_service!(ffi_service_main, my_service_main);
    
    fn my_service_main(arguments: Vec<OsString>) {
        let _ = arguments;
        let (shutdown_tx, shutdown_rx) = mpsc::channel();
    
        let _event_handler = move |control_event| -> ServiceControlHandlerResult {
            match control_event {
                ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
    
                ServiceControl::Stop => {
                    shutdown_tx.send(()).unwrap();
                    ServiceControlHandlerResult::NoError
                }
    
                ServiceControl::UserEvent(code) => {
                    if code.to_raw() == 130 {
                        shutdown_tx.send(()).unwrap();
                    }
                    ServiceControlHandlerResult::NoError
                }
    
                _ => ServiceControlHandlerResult::NotImplemented,
            }
        };
    
        let status_handle = service_control_handler::register(SERVICE_NAME, _event_handler);
        let status_handle = status_handle.unwrap();
        let _ = status_handle.set_service_status(ServiceStatus {
            service_type: ServiceType::OWN_PROCESS,
            current_state: ServiceState::Running,
            controls_accepted: ServiceControlAccept::STOP,
            exit_code: ServiceExitCode::Win32(0),
            checkpoint: 0,
            wait_hint: Duration::default(),
            process_id: None,
        });
    
        loop {
            unsafe {
                let mut a: SYSTEM_POWER_POLICY = std::mem::zeroed();
                let status = CallNtPowerInformation(
                    POWER_INFORMATION_LEVEL { 0: 8 },
                    None,
                    0,
                    Some(&mut a as *mut SYSTEM_POWER_POLICY as *mut c_void),
                    size_of::<SYSTEM_POWER_POLICY>() as u32,
                );
                if status != STATUS_SUCCESS {
                    println!("获取电源状态失败: {:x} !", status.0);
                    return;
                }
                if a.LidClose.Action.0 == 0 {
                    println!("状态已为0, 忽略");
                    return;
                } else {
                    println!("状态为{:x}", a.LidClose.Action.0);
                    a.LidClose.Action.0 = 0;
                    let status = CallNtPowerInformation(
                        POWER_INFORMATION_LEVEL { 0: 0 },
                        Some(&mut a as *mut SYSTEM_POWER_POLICY as *mut c_void),
                        size_of::<SYSTEM_POWER_POLICY>() as u32,
                        None,
                        0,
                    );
                    if status != STATUS_SUCCESS {
                        println!("设置ac电源状态失败: {:x} !", status.0);
                        return;
                    } else {
                        println!("设置AC电源状态成功");
                    }
                    let status = CallNtPowerInformation(
                        POWER_INFORMATION_LEVEL { 0: 1 },
                        Some(&mut a as *mut SYSTEM_POWER_POLICY as *mut c_void),
                        size_of::<SYSTEM_POWER_POLICY>() as u32,
                        None,
                        0,
                    );
                    if status != STATUS_SUCCESS {
                        println!("设置dc电源状态失败: {:x} !", status.0);
                        return;
                    } else {
                        println!("设置DC电源状态成功");
                    }
                }
            }
            match shutdown_rx.recv_timeout(Duration::from_secs(5)) {
                Ok(_) | Err(mpsc::RecvTimeoutError::Disconnected) => break,
                Err(mpsc::RecvTimeoutError::Timeout) => (),
            }
        }
    
        let _ = status_handle.set_service_status(ServiceStatus {
            service_type: ServiceType::OWN_PROCESS,
            current_state: ServiceState::Stopped,
            controls_accepted: ServiceControlAccept::empty(),
            exit_code: ServiceExitCode::Win32(0),
            checkpoint: 0,
            wait_hint: Duration::default(),
            process_id: None,
        });
    }
    
    
    
  • 相关阅读:
    深入解析C++的auto自动类型推导
    Android的FragmentManager介绍以及它管理Fragment的流程
    音乐随行,公网畅享,群辉Audiostation给你带来听歌新体验!
    机组实践实验8——使用CMStudio设计基于基本模型机微程序指令(1)
    vue 大文件切片下载
    大数据实战之前戏
    matplotlib python 画图介绍
    nginx日志进行统计分析 log分析
    操作系统—线程的实现方式和多线程模型
    【算法-回溯法】N皇后问题
  • 原文地址:https://blog.csdn.net/LeoForBest/article/details/142287713