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 )
c 复制代码
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示例提取了一个模板,只有替换编写两处/* */代码

rust 复制代码
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获取配置信息,不符合当前设置执行修改

完整如下

rust 复制代码
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,
    });
}
相关推荐
数据知道10 小时前
claw-code 源码分析:从 TypeScript 心智到 Python/Rust——跨栈移植时类型、边界与错误模型怎么对齐?
python·ai·rust·typescript·claude code·claw code
handsomestWei12 小时前
claude-code在win环境安装使用
windows·ai编程·claude·安装配置·cc-switch
秦时明月之君临天下12 小时前
Windows如何删除任务管理器中的某个服务?
windows
麦田里的守望者_zhg13 小时前
Windows 执行 wsl --update 报错 1603:注册表权限导致 WSL 安装损坏问题排查与修复
windows
❆VE❆14 小时前
Claude Code 安装与配置完整指南(Windows)
windows·claude code
航Hang*15 小时前
VMware vSphere 云平台运维与管理基础——第5章:VMware vSphere 5.5 高级特性
运维·服务器·开发语言·windows·学习·虚拟化
Mapleay15 小时前
Ubuntu 源的重要性!之 libgmp-dev 无法安装
linux·服务器·windows
humors22115 小时前
微软工具包下载网址
windows·microsoft·微软·office·工具包·sysintervals
寺中人16 小时前
硬盘提示初始化的损坏,手动恢复MBR及EBR分区教程
windows·工具·硬盘修复
冷色系里的一抹暖调16 小时前
OpenClaw Docker 部署避坑指南:服务启动成功但网页打不开?
人工智能·windows·docker·ai·容器·opencode