use std::{mem, path::Path, ptr};
use windows_sys::Win32::{
Foundation::{ERROR_MORE_DATA, ERROR_SUCCESS},
System::RestartManager::{
CCH_RM_MAX_APP_NAME, CCH_RM_SESSION_KEY, RM_PROCESS_INFO, RmEndSession, RmGetList,
RmRegisterResources, RmStartSession,
},
};
use crate::{
error::{Error, Result},
process::Process,
wide::{from_wide, wide_path},
};
pub fn locking_processes(files: &[&Path]) -> Result<Vec<Process>> {
if files.is_empty() {
return Ok(Vec::new());
}
unsafe {
let mut session = 0u32;
let mut key = [0u16; CCH_RM_SESSION_KEY as usize + 1];
let status = RmStartSession(&mut session, 0, key.as_mut_ptr());
if status != ERROR_SUCCESS {
return Err(Error::code("RmStartSession", status));
}
let session = SessionGuard(session);
let wide: Vec<Vec<u16>> = files.iter().map(|p| wide_path(p)).collect();
let pointers: Vec<*const u16> = wide.iter().map(|w| w.as_ptr()).collect();
let status = RmRegisterResources(
session.0,
pointers.len() as u32,
pointers.as_ptr(),
0,
ptr::null(),
0,
ptr::null(),
);
if status != ERROR_SUCCESS {
return Err(Error::code("RmRegisterResources", status));
}
let mut capacity = 16u32;
loop {
let mut needed = 0u32;
let mut count = capacity;
let mut reason = 0u32;
let mut info: Vec<RM_PROCESS_INFO> = vec![mem::zeroed(); capacity as usize];
let status = RmGetList(
session.0,
&mut needed,
&mut count,
info.as_mut_ptr(),
&mut reason,
);
if status == ERROR_MORE_DATA {
capacity = needed.max(capacity * 2);
continue;
}
if status != ERROR_SUCCESS {
return Err(Error::code("RmGetList", status));
}
info.truncate(count as usize);
return Ok(info
.iter()
.map(|entry| Process {
id: entry.Process.dwProcessId,
name: app_name(entry),
image: None,
})
.collect());
}
}
}
fn app_name(entry: &RM_PROCESS_INFO) -> String {
let name = from_wide(&entry.strAppName[..CCH_RM_MAX_APP_NAME as usize]);
if name.is_empty() {
format!("process {}", entry.Process.dwProcessId)
} else {
name
}
}
struct SessionGuard(u32);
impl Drop for SessionGuard {
fn drop(&mut self) {
unsafe { RmEndSession(self.0) };
}
}