windows_erg/service/
service.rs1use std::time::{Duration, Instant};
2
3use windows::Win32::Foundation::ERROR_SERVICE_DOES_NOT_EXIST;
4use windows::Win32::System::Services::{
5 CloseServiceHandle, ControlService, QueryServiceStatusEx, SC_HANDLE, SC_STATUS_PROCESS_INFO,
6 SERVICE_STATUS, SERVICE_STATUS_PROCESS, StartServiceW,
7};
8use windows::core::PCWSTR;
9
10use super::status::ServiceStatus;
11use super::types::{ServiceAccess, ServiceControl, ServiceState};
12use crate::Result;
13use crate::error::{
14 Error, ServiceError, ServiceInvalidStateError, ServiceNotFoundError, ServiceOperationError,
15};
16
17pub struct Service {
19 handle: SC_HANDLE,
20 name: String,
21}
22
23impl Service {
24 pub(crate) fn new(handle: SC_HANDLE, name: String) -> Self {
25 Service { handle, name }
26 }
27
28 pub fn open(name: &str) -> Result<Self> {
30 super::manager::ServiceManager::connect()?
31 .open_with_access(name, ServiceAccess::QueryStatus)
32 }
33
34 pub fn open_with_access(name: &str, access: ServiceAccess) -> Result<Self> {
36 super::manager::ServiceManager::connect()?.open_with_access(name, access)
37 }
38
39 pub fn name(&self) -> &str {
41 self.name.as_str()
42 }
43
44 pub fn query(&self) -> Result<ServiceStatus> {
46 let mut work_buffer = Vec::with_capacity(std::mem::size_of::<SERVICE_STATUS_PROCESS>());
47 self.query_with_buffer(&mut work_buffer)
48 }
49
50 pub fn query_with_buffer(&self, work_buffer: &mut Vec<u8>) -> Result<ServiceStatus> {
52 work_buffer.clear();
53
54 let mut bytes_needed = 0u32;
55 let _ = unsafe {
56 QueryServiceStatusEx(self.handle, SC_STATUS_PROCESS_INFO, None, &mut bytes_needed)
57 };
58
59 let required = (bytes_needed as usize).max(std::mem::size_of::<SERVICE_STATUS_PROCESS>());
60 if work_buffer.len() < required {
61 work_buffer.resize(required, 0);
62 }
63
64 unsafe {
65 QueryServiceStatusEx(
66 self.handle,
67 SC_STATUS_PROCESS_INFO,
68 Some(work_buffer),
69 &mut bytes_needed,
70 )
71 }
72 .map_err(|e| {
73 if e.code().0 == ERROR_SERVICE_DOES_NOT_EXIST.to_hresult().0 {
74 return Error::Service(ServiceError::NotFound(ServiceNotFoundError::with_code(
75 self.name.clone(),
76 e.code().0,
77 )));
78 }
79
80 Error::Service(ServiceError::OperationFailed(
81 ServiceOperationError::with_code(
82 self.name.clone(),
83 "query",
84 "QueryServiceStatusEx failed",
85 e.code().0,
86 ),
87 ))
88 })?;
89
90 let raw = unsafe { &*(work_buffer.as_ptr() as *const SERVICE_STATUS_PROCESS) };
91 Ok(ServiceStatus::from_status_process(
92 self.name.clone(),
93 None,
94 raw,
95 ))
96 }
97
98 pub fn start(&self) -> Result<()> {
100 unsafe { StartServiceW(self.handle, None) }.map_err(|e| {
101 Error::Service(ServiceError::OperationFailed(
102 ServiceOperationError::with_code(
103 self.name.clone(),
104 "start",
105 "StartServiceW failed",
106 e.code().0,
107 ),
108 ))
109 })
110 }
111
112 pub fn stop(&self) -> Result<()> {
114 self.send_control(ServiceControl::Stop)
115 }
116
117 pub fn send_control(&self, control: ServiceControl) -> Result<()> {
119 let mut status = SERVICE_STATUS::default();
120 unsafe { ControlService(self.handle, control.to_windows(), &mut status) }.map_err(|e| {
121 Error::Service(ServiceError::OperationFailed(
122 ServiceOperationError::with_code(
123 self.name.clone(),
124 control.operation_name(),
125 "ControlService failed",
126 e.code().0,
127 ),
128 ))
129 })
130 }
131
132 pub fn restart(&self, timeout: Duration) -> Result<()> {
134 self.stop()?;
135 self.wait_for_state(ServiceState::Stopped, timeout)?;
136 self.start()
137 }
138
139 fn wait_for_state(&self, desired: ServiceState, timeout: Duration) -> Result<()> {
140 let start = Instant::now();
141 let mut work_buffer = Vec::with_capacity(std::mem::size_of::<SERVICE_STATUS_PROCESS>());
142 let mut last_checkpoint = 0u32;
143 let mut checkpoint_stale_since = Instant::now();
144
145 while start.elapsed() <= timeout {
146 let status = self.query_with_buffer(&mut work_buffer)?;
147 if status.state == desired {
148 return Ok(());
149 }
150
151 if status.checkpoint != last_checkpoint {
152 last_checkpoint = status.checkpoint;
153 checkpoint_stale_since = Instant::now();
154 }
155
156 if checkpoint_stale_since.elapsed() > timeout {
157 break;
158 }
159
160 let wait_hint_ms = status.wait_hint_ms.clamp(100, 10_000);
161 let poll_ms = (wait_hint_ms / 10).clamp(100, 1000);
162 std::thread::sleep(Duration::from_millis(poll_ms as u64));
163 }
164
165 Err(Error::Service(ServiceError::InvalidState(
166 ServiceInvalidStateError::new(
167 self.name.clone(),
168 desired.as_str(),
169 "timed out waiting for state transition",
170 ),
171 )))
172 }
173}
174
175impl Drop for Service {
176 fn drop(&mut self) {
177 let _ = unsafe { CloseServiceHandle(self.handle) };
178 }
179}
180
181pub(crate) fn as_pcwstr(wide: &[u16]) -> PCWSTR {
182 PCWSTR(wide.as_ptr())
183}