use std::{
fmt::Debug,
io,
os::windows::prelude::{
AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle,
},
process::Child,
};
use winapi::{
shared::minwindef::FALSE,
um::{
processthreadsapi::OpenProcess,
winnt::{
PROCESS_CREATE_THREAD, PROCESS_DUP_HANDLE, PROCESS_QUERY_INFORMATION,
PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_TERMINATE, PROCESS_VM_READ,
PROCESS_VM_WRITE, SYNCHRONIZE,
},
},
};
use crate::{
error::ProcessError, raw, BorrowedProcess, FromRawProcessHandle, Process, ProcessHandle,
RawProcessHandle,
};
pub type OwnedProcess = Process<OwnedHandle>;
impl ProcessHandle for OwnedHandle {
fn try_clone(&self) -> io::Result<Self>
where
Self: Sized,
{
self.try_clone()
}
}
impl FromRawProcessHandle for OwnedHandle {
unsafe fn from_raw_process_handle(handle: RawProcessHandle) -> Self {
unsafe { FromRawHandle::from_raw_handle(handle) }
}
}
#[allow(warnings)]
unsafe impl Send for OwnedProcess {}
#[allow(warnings)]
unsafe impl Sync for OwnedProcess {}
impl From<Child> for OwnedProcess {
fn from(child: Child) -> Self {
Self::from_child(child)
}
}
impl TryFrom<BorrowedProcess<'_>> for OwnedProcess {
type Error = ProcessError;
fn try_from(process: BorrowedProcess<'_>) -> Result<Self, Self::Error> {
process.try_to_owned()
}
}
impl OwnedProcess {
pub fn from_pid(pid: u32) -> Result<OwnedProcess, ProcessError> {
unsafe {
Self::from_pid_with_access(
pid,
PROCESS_CREATE_THREAD
| PROCESS_DUP_HANDLE
| PROCESS_QUERY_INFORMATION
| PROCESS_TERMINATE
| PROCESS_VM_READ
| PROCESS_VM_WRITE
| SYNCHRONIZE
| PROCESS_QUERY_LIMITED_INFORMATION,
)
}
}
pub unsafe fn from_pid_with_access(
pid: u32,
access: u32,
) -> Result<OwnedProcess, ProcessError> {
let handle = unsafe { OpenProcess(access, FALSE, pid) };
if handle.is_null() {
return Err(io::Error::last_os_error().into());
}
Ok(unsafe { OwnedProcess::from_raw_handle(handle) })
}
pub fn all() -> Result<impl Iterator<Item = OwnedProcess>, ProcessError> {
let iter = raw::iter_process_ids()?
.map(OwnedProcess::from_pid)
.filter_map(|r| r.ok());
Ok(iter)
}
pub fn find_all_by_name(
name: impl AsRef<str>,
) -> Result<impl Iterator<Item = OwnedProcess>, ProcessError> {
let target_name = name.as_ref().to_ascii_lowercase();
let iter = Self::all()?.filter(move |process| {
let name = match process.base_name() {
Ok(name) => name,
Err(_) => return false,
};
name.to_ascii_lowercase().contains(&target_name)
});
Ok(iter)
}
pub fn find_first_by_name(name: impl AsRef<str>) -> Result<Option<OwnedProcess>, ProcessError> {
Ok(Self::find_all_by_name(name)?.next())
}
#[must_use]
pub fn from_child(child: Child) -> OwnedProcess {
unsafe { OwnedProcess::from_raw_handle(child.into_raw_handle()) }
}
#[must_use]
#[doc(hidden)]
pub unsafe fn borrow_static(&self) -> BorrowedProcess<'static> {
unsafe { BorrowedProcess::from_handle(BorrowedHandle::borrow_raw(self.as_raw_handle())) }
}
#[allow(clippy::must_use_candidate)]
pub fn leak(self) -> BorrowedProcess<'static> {
unsafe { self.borrow_static() }
}
#[must_use]
pub const fn kill_on_drop(self) -> ProcessKillGuard {
ProcessKillGuard(self)
}
}
#[derive(Debug, shrinkwraprs::Shrinkwrap)]
#[shrinkwrap(mutable)]
pub struct ProcessKillGuard(pub OwnedProcess);
impl Drop for ProcessKillGuard {
fn drop(&mut self) {
let _ = self.0.kill();
}
}