windows_erg/service/
manager.rs1use windows::Win32::Foundation::{ERROR_ACCESS_DENIED, ERROR_SERVICE_DOES_NOT_EXIST};
2use windows::Win32::System::Services::{
3 CloseServiceHandle, ENUM_SERVICE_STATUS_PROCESSW, EnumServicesStatusExW, OpenSCManagerW,
4 OpenServiceW, SC_ENUM_PROCESS_INFO, SC_HANDLE, SC_MANAGER_CONNECT,
5 SC_MANAGER_ENUMERATE_SERVICE, SERVICE_STATE_ALL, SERVICE_WIN32,
6};
7
8use super::service::{Service, as_pcwstr};
9use super::status::ServiceStatus;
10use super::types::{ServiceAccess, ServiceManagerAccess};
11use crate::Result;
12use crate::error::{
13 AccessDeniedError, Error, InvalidParameterError, NotFoundError, ServiceError,
14 ServiceManagerError, ServiceNotFoundError,
15};
16use crate::utils::to_utf16_nul;
17
18pub struct ServiceManager {
20 handle: SC_HANDLE,
21}
22
23impl ServiceManager {
24 pub fn connect() -> Result<Self> {
26 let rights = SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE;
27 Self::connect_with_access(ServiceManagerAccess::Custom(rights))
28 }
29
30 pub fn connect_with_access(access: ServiceManagerAccess) -> Result<Self> {
32 let handle = unsafe { OpenSCManagerW(None, None, access.to_windows()) }.map_err(|e| {
33 if e.code().0 == ERROR_ACCESS_DENIED.to_hresult().0 {
34 return Error::AccessDenied(AccessDeniedError::new("service manager", "connect"));
35 }
36
37 Error::Service(ServiceError::ManagerError(ServiceManagerError::with_code(
38 "connect",
39 "OpenSCManagerW failed",
40 e.code().0,
41 )))
42 })?;
43
44 Ok(ServiceManager { handle })
45 }
46
47 pub fn open(&self, name: &str) -> Result<Service> {
49 let access = ServiceAccess::QueryStatus.to_windows()
50 | ServiceAccess::Start.to_windows()
51 | ServiceAccess::Stop.to_windows()
52 | ServiceAccess::PauseContinue.to_windows();
53 self.open_with_access(name, ServiceAccess::Custom(access))
54 }
55
56 pub fn open_with_access(&self, name: &str, access: ServiceAccess) -> Result<Service> {
58 if name.trim().is_empty() {
59 return Err(Error::InvalidParameter(InvalidParameterError::new(
60 "name",
61 "service name cannot be empty",
62 )));
63 }
64
65 let name_wide = to_utf16_nul(name);
66 let handle =
67 unsafe { OpenServiceW(self.handle, as_pcwstr(&name_wide), access.to_windows()) }
68 .map_err(|e| {
69 if e.code().0 == ERROR_SERVICE_DOES_NOT_EXIST.to_hresult().0 {
70 return Error::NotFound(NotFoundError::new("service", name.to_owned()));
71 }
72 if e.code().0 == ERROR_ACCESS_DENIED.to_hresult().0 {
73 return Error::AccessDenied(AccessDeniedError::new(
74 name.to_owned(),
75 "open",
76 ));
77 }
78
79 Error::Service(ServiceError::NotFound(ServiceNotFoundError::with_code(
80 name.to_owned(),
81 e.code().0,
82 )))
83 })?;
84
85 Ok(Service::new(handle, name.to_owned()))
86 }
87
88 pub fn list(&self) -> Result<Vec<ServiceStatus>> {
90 let mut out_services = Vec::with_capacity(256);
91 self.list_with_buffer(&mut out_services)?;
92 Ok(out_services)
93 }
94
95 pub fn list_with_buffer(&self, out_services: &mut Vec<ServiceStatus>) -> Result<usize> {
97 self.list_with_filter(out_services, |_| true)
98 }
99
100 pub fn list_with_filter<F>(
102 &self,
103 out_services: &mut Vec<ServiceStatus>,
104 filter: F,
105 ) -> Result<usize>
106 where
107 F: Fn(&ServiceStatus) -> bool,
108 {
109 out_services.clear();
110
111 let mut work_buffer = Vec::new();
112 let mut bytes_needed = 0u32;
113 let mut services_returned = 0u32;
114
115 let _ = unsafe {
116 EnumServicesStatusExW(
117 self.handle,
118 SC_ENUM_PROCESS_INFO,
119 SERVICE_WIN32,
120 SERVICE_STATE_ALL,
121 None,
122 &mut bytes_needed,
123 &mut services_returned,
124 None,
125 None,
126 )
127 };
128
129 if bytes_needed == 0 {
130 return Ok(0);
131 }
132
133 work_buffer.resize(bytes_needed as usize, 0);
134
135 unsafe {
136 EnumServicesStatusExW(
137 self.handle,
138 SC_ENUM_PROCESS_INFO,
139 SERVICE_WIN32,
140 SERVICE_STATE_ALL,
141 Some(&mut work_buffer),
142 &mut bytes_needed,
143 &mut services_returned,
144 None,
145 None,
146 )
147 }
148 .map_err(|e| {
149 Error::Service(ServiceError::ManagerError(ServiceManagerError::with_code(
150 "enumerate",
151 "EnumServicesStatusExW failed",
152 e.code().0,
153 )))
154 })?;
155
156 let entries = unsafe {
157 std::slice::from_raw_parts(
158 work_buffer.as_ptr() as *const ENUM_SERVICE_STATUS_PROCESSW,
159 services_returned as usize,
160 )
161 };
162
163 for entry in entries {
164 let status = ServiceStatus::from_enum_status(entry);
165 if filter(&status) {
166 out_services.push(status);
167 }
168 }
169
170 Ok(out_services.len())
171 }
172}
173
174impl Drop for ServiceManager {
175 fn drop(&mut self) {
176 let _ = unsafe { CloseServiceHandle(self.handle) };
177 }
178}