use crate::os::registry::wide;
use windows::core::PCWSTR;
use windows::Win32::System::Services::*;
const USER_SERVICE_TYPES: u32 = 0xF0;
const PREFIX: &str = "WpnUserService_";
pub fn restart_notification_service() -> usize {
let names = enumerate_user_services();
let mut ok = 0;
for name in names {
if restart_one(&name) {
ok += 1;
}
}
ok
}
fn enumerate_user_services() -> Vec<String> {
let mut out = Vec::new();
unsafe {
let Ok(scm) = OpenSCManagerW(
None,
None,
SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT,
) else {
return out;
};
let mut needed = 0u32;
let mut returned = 0u32;
let mut resume = 0u32;
let _ = EnumServicesStatusExW(
scm,
SC_ENUM_PROCESS_INFO,
ENUM_SERVICE_TYPE(USER_SERVICE_TYPES),
SERVICE_STATE_ALL,
None,
&mut needed,
&mut returned,
Some(&mut resume),
None,
);
if needed > 0 {
let mut buf = vec![0u8; needed as usize];
if EnumServicesStatusExW(
scm,
SC_ENUM_PROCESS_INFO,
ENUM_SERVICE_TYPE(USER_SERVICE_TYPES),
SERVICE_STATE_ALL,
Some(&mut buf),
&mut needed,
&mut returned,
Some(&mut resume),
None,
)
.is_ok()
{
let entries = std::slice::from_raw_parts(
buf.as_ptr() as *const ENUM_SERVICE_STATUS_PROCESSW,
returned as usize,
);
for e in entries {
if let Ok(name) = e.lpServiceName.to_string() {
if name.starts_with(PREFIX) {
out.push(name);
}
}
}
}
}
let _ = CloseServiceHandle(scm);
}
out
}
fn restart_one(name: &str) -> bool {
unsafe {
let Ok(scm) = OpenSCManagerW(None, None, SC_MANAGER_CONNECT) else {
return false;
};
let w = wide(name);
let svc = OpenServiceW(
scm,
PCWSTR(w.as_ptr()),
SERVICE_START | SERVICE_STOP | SERVICE_QUERY_STATUS,
);
let result = match svc {
Ok(svc) => {
let mut status = SERVICE_STATUS::default();
let _ = ControlService(svc, SERVICE_CONTROL_STOP, &mut status);
wait_for(svc, SERVICE_STOPPED);
for _ in 0..20 {
if StartServiceW(svc, None).is_ok() {
break;
}
if current_state(svc) == Some(SERVICE_RUNNING) {
break;
}
std::thread::sleep(std::time::Duration::from_millis(100));
}
let ok = wait_for(svc, SERVICE_RUNNING);
let _ = CloseServiceHandle(svc);
ok
}
Err(_) => false,
};
let _ = CloseServiceHandle(scm);
result
}
}
unsafe fn current_state(svc: SC_HANDLE) -> Option<SERVICE_STATUS_CURRENT_STATE> {
let mut st = SERVICE_STATUS::default();
QueryServiceStatus(svc, &mut st)
.ok()
.map(|_| st.dwCurrentState)
}
unsafe fn wait_for(svc: SC_HANDLE, want: SERVICE_STATUS_CURRENT_STATE) -> bool {
for _ in 0..50 {
match current_state(svc) {
Some(s) if s == want => return true,
None => return false,
_ => std::thread::sleep(std::time::Duration::from_millis(100)),
}
}
false
}