windows_service/
service_control_handler.rs

1use 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/// A struct that holds a unique token for updating the status of the corresponding service.
15#[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    /// Report the new service status to the system.
24    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    /// Get access to the raw handle to use in other Windows APIs
37    fn as_raw_handle(&self) -> RawHandle {
38        self.0 as _
39    }
40}
41
42// Underlying SERVICE_STATUS_HANDLE is thread safe.
43// See remarks section for more info:
44// https://msdn.microsoft.com/en-us/library/windows/desktop/ms686241(v=vs.85).aspx
45unsafe impl Send for ServiceStatusHandle {}
46unsafe impl Sync for ServiceStatusHandle {}
47
48/// Abstraction over the return value of service control handler.
49/// The meaning of each of variants in this enum depends on the type of received event.
50///
51/// See the "Return value" section of corresponding MSDN article for more info:
52///
53/// <https://msdn.microsoft.com/en-us/library/windows/desktop/ms683241(v=vs.85).aspx>
54#[derive(Debug)]
55pub enum ServiceControlHandlerResult {
56    /// Either used to acknowledge the call or grant the permission in advanced events.
57    NoError,
58    /// The received event is not implemented.
59    NotImplemented,
60    /// This variant is used to deny permission and return the reason error code in advanced
61    /// events.
62    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
75/// Register a closure for receiving service events.
76///
77/// Returns [`ServiceStatusHandle`] that can be used to report the service status back to the
78/// system.
79///
80/// # Example
81///
82/// ```rust,no_run
83/// use std::ffi::OsString;
84/// use windows_service::service::ServiceControl;
85/// use windows_service::service_control_handler::{self, ServiceControlHandlerResult};
86///
87/// fn my_service_main(_arguments: Vec<OsString>) {
88///     if let Err(_e) = run_service() {
89///         // Handle errors...
90///     }
91/// }
92///
93/// fn run_service() -> windows_service::Result<()> {
94///     let event_handler = move |control_event| -> ServiceControlHandlerResult {
95///         match control_event {
96///             ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
97///             _ => ServiceControlHandlerResult::NotImplemented,
98///         }
99///     };
100///     let status_handle = service_control_handler::register("my_service_name", event_handler)?;
101///     Ok(())
102/// }
103///
104/// # fn main() {}
105/// ```
106pub fn register<F>(service_name: impl AsRef<OsStr>, event_handler: F) -> Result<ServiceStatusHandle>
107where
108    F: FnMut(ServiceControl) -> ServiceControlHandlerResult + 'static + Send,
109{
110    // Move closure to heap.
111    let heap_event_handler: Box<F> = Box::new(event_handler);
112
113    // Important: leak the Box<F> which will be released in `service_control_handler`.
114    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        // Release the `event_handler` in case of an error.
128        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/// Static service control handler
136#[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    // Important: cast context to &mut F without taking ownership.
147    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            // Important: release context upon Stop, Shutdown or Preshutdown at the end of the
159            // service lifecycle.
160            if need_release {
161                let _: Box<F> = unsafe { Box::from_raw(context as *mut F) };
162            }
163
164            return_code
165        }
166
167        // Report all unknown control commands as unimplemented
168        Err(_) => ServiceControlHandlerResult::NotImplemented.to_raw(),
169    }
170}