use std::ffi::OsStr;
use std::io;
use std::os::raw::c_void;
use std::os::windows::io::{AsRawHandle, RawHandle};
use widestring::WideCString;
use windows_sys::Win32::{
Foundation::{ERROR_CALL_NOT_IMPLEMENTED, NO_ERROR},
System::Services,
};
use crate::service::{ServiceControl, ServiceStatus};
use crate::{Error, Result};
#[derive(Debug, Clone, Copy)]
pub struct ServiceStatusHandle(Services::SERVICE_STATUS_HANDLE);
impl ServiceStatusHandle {
fn from_handle(handle: Services::SERVICE_STATUS_HANDLE) -> Self {
ServiceStatusHandle(handle)
}
pub fn set_service_status(&self, service_status: ServiceStatus) -> crate::Result<()> {
let raw_service_status = service_status.to_raw();
let result = unsafe { Services::SetServiceStatus(self.0, &raw_service_status) };
if result == 0 {
Err(Error::Winapi(io::Error::last_os_error()))
} else {
Ok(())
}
}
}
impl AsRawHandle for ServiceStatusHandle {
fn as_raw_handle(&self) -> RawHandle {
self.0 as _
}
}
unsafe impl Send for ServiceStatusHandle {}
unsafe impl Sync for ServiceStatusHandle {}
#[derive(Debug)]
pub enum ServiceControlHandlerResult {
NoError,
NotImplemented,
Other(u32),
}
impl ServiceControlHandlerResult {
pub fn to_raw(&self) -> u32 {
match *self {
ServiceControlHandlerResult::NoError => NO_ERROR,
ServiceControlHandlerResult::NotImplemented => ERROR_CALL_NOT_IMPLEMENTED,
ServiceControlHandlerResult::Other(code) => code,
}
}
}
pub fn register<F>(service_name: impl AsRef<OsStr>, event_handler: F) -> Result<ServiceStatusHandle>
where
F: FnMut(ServiceControl) -> ServiceControlHandlerResult + 'static + Send,
{
let heap_event_handler: Box<F> = Box::new(event_handler);
let context: *mut F = Box::into_raw(heap_event_handler);
let service_name = WideCString::from_os_str(service_name)
.map_err(|_| Error::ArgumentHasNulByte("service name"))?;
let status_handle = unsafe {
Services::RegisterServiceCtrlHandlerExW(
service_name.as_ptr(),
Some(service_control_handler::<F>),
context as *mut c_void,
)
};
if status_handle.is_null() {
let _: Box<F> = unsafe { Box::from_raw(context) };
Err(Error::Winapi(io::Error::last_os_error()))
} else {
Ok(ServiceStatusHandle::from_handle(status_handle))
}
}
#[allow(dead_code)]
extern "system" fn service_control_handler<F>(
control: u32,
event_type: u32,
event_data: *mut c_void,
context: *mut c_void,
) -> u32
where
F: FnMut(ServiceControl) -> ServiceControlHandlerResult,
{
let event_handler: &mut F = unsafe { &mut *(context as *mut F) };
match unsafe { ServiceControl::from_raw(control, event_type, event_data) } {
Ok(service_control) => {
let need_release = matches!(
service_control,
ServiceControl::Stop | ServiceControl::Shutdown | ServiceControl::Preshutdown,
);
let return_code = event_handler(service_control).to_raw();
if need_release {
let _: Box<F> = unsafe { Box::from_raw(context as *mut F) };
}
return_code
}
Err(_) => ServiceControlHandlerResult::NotImplemented.to_raw(),
}
}