文章目录
Rust编写Windows服务
编写Windows服务可选语言很多, 其中C#最简单。本着练手Rust语言,尝试用Rust编写一个服务。
一:Windows服务程序大致原理
参考官网C/C++创建服务流程
https://learn.microsoft.com/zh-cn/windows/win32/services/service-program-tasks
- 编写服务程序的main函数
- main函数中,填充数据结构
DispatchTable
: [[服务名,ServiceMain函数], [NULL, NULL]] - 调用
StartServiceCtrlDispatcher( DispatchTable )
- main函数中,填充数据结构
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 函数
- 注册控制处理函数
RegisterServiceCtrlHandler(SvcCtrlHandler )
- 设置服务状态,SERVICE_START_PENDING
- 做一些预备工作,比如初始化日志/注册事件等
- 设置服务状态,SERVICE_START_RUNNING
- 开始loop处理我们自己的代码(loop循环中可以接受3中注册的事件,当通知停止时退出循环)
- 注册控制处理函数
- 编写控件处理程序函数
- 接受事件管理器发送的消息并处理,比如收到
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,
});
}