windows_service/
service_control_handler.rs1use std::ffi::OsStr;
2use std::io;
3use std::os::raw::c_void;
4use std::os::windows::io::{AsRawHandle, RawHandle};
5use widestring::WideCString;
6use windows_sys::Win32::{
7 Foundation::{ERROR_CALL_NOT_IMPLEMENTED, NO_ERROR},
8 System::Services,
9};
10
11use crate::service::{ServiceControl, ServiceStatus};
12use crate::{Error, Result};
13
14#[derive(Debug, Clone, Copy)]
16pub struct ServiceStatusHandle(Services::SERVICE_STATUS_HANDLE);
17
18impl ServiceStatusHandle {
19 fn from_handle(handle: Services::SERVICE_STATUS_HANDLE) -> Self {
20 ServiceStatusHandle(handle)
21 }
22
23 pub fn set_service_status(&self, service_status: ServiceStatus) -> crate::Result<()> {
25 let raw_service_status = service_status.to_raw();
26 let result = unsafe { Services::SetServiceStatus(self.0, &raw_service_status) };
27 if result == 0 {
28 Err(Error::Winapi(io::Error::last_os_error()))
29 } else {
30 Ok(())
31 }
32 }
33}
34
35impl AsRawHandle for ServiceStatusHandle {
36 fn as_raw_handle(&self) -> RawHandle {
38 self.0 as _
39 }
40}
41
42unsafe impl Send for ServiceStatusHandle {}
46unsafe impl Sync for ServiceStatusHandle {}
47
48#[derive(Debug)]
55pub enum ServiceControlHandlerResult {
56 NoError,
58 NotImplemented,
60 Other(u32),
63}
64
65impl ServiceControlHandlerResult {
66 pub fn to_raw(&self) -> u32 {
67 match *self {
68 ServiceControlHandlerResult::NoError => NO_ERROR,
69 ServiceControlHandlerResult::NotImplemented => ERROR_CALL_NOT_IMPLEMENTED,
70 ServiceControlHandlerResult::Other(code) => code,
71 }
72 }
73}
74
75pub fn register<F>(service_name: impl AsRef<OsStr>, event_handler: F) -> Result<ServiceStatusHandle>
107where
108 F: FnMut(ServiceControl) -> ServiceControlHandlerResult + 'static + Send,
109{
110 let heap_event_handler: Box<F> = Box::new(event_handler);
112
113 let context: *mut F = Box::into_raw(heap_event_handler);
115
116 let service_name = WideCString::from_os_str(service_name)
117 .map_err(|_| Error::ArgumentHasNulByte("service name"))?;
118 let status_handle = unsafe {
119 Services::RegisterServiceCtrlHandlerExW(
120 service_name.as_ptr(),
121 Some(service_control_handler::<F>),
122 context as *mut c_void,
123 )
124 };
125
126 if status_handle.is_null() {
127 let _: Box<F> = unsafe { Box::from_raw(context) };
129 Err(Error::Winapi(io::Error::last_os_error()))
130 } else {
131 Ok(ServiceStatusHandle::from_handle(status_handle))
132 }
133}
134
135#[allow(dead_code)]
137extern "system" fn service_control_handler<F>(
138 control: u32,
139 event_type: u32,
140 event_data: *mut c_void,
141 context: *mut c_void,
142) -> u32
143where
144 F: FnMut(ServiceControl) -> ServiceControlHandlerResult,
145{
146 let event_handler: &mut F = unsafe { &mut *(context as *mut F) };
148
149 match unsafe { ServiceControl::from_raw(control, event_type, event_data) } {
150 Ok(service_control) => {
151 let need_release = matches!(
152 service_control,
153 ServiceControl::Stop | ServiceControl::Shutdown | ServiceControl::Preshutdown,
154 );
155
156 let return_code = event_handler(service_control).to_raw();
157
158 if need_release {
161 let _: Box<F> = unsafe { Box::from_raw(context as *mut F) };
162 }
163
164 return_code
165 }
166
167 Err(_) => ServiceControlHandlerResult::NotImplemented.to_raw(),
169 }
170}