use std::{
hash::{Hash, Hasher},
io,
ops::{Deref, DerefMut},
os::windows::{
prelude::{
AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle,
RawHandle,
},
raw::HANDLE,
},
process::Child,
};
use sysinfo::{PidExt, ProcessExt, SystemExt};
use winapi::{
shared::minwindef::{DWORD, FALSE},
um::{
processthreadsapi::OpenProcess,
winnt::{
PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION,
PROCESS_VM_READ, PROCESS_VM_WRITE,
},
},
};
use crate::ProcessRef;
pub type ProcessHandle = std::os::windows::raw::HANDLE;
pub const PROCESS_INJECTION_ACCESS: DWORD = PROCESS_CREATE_THREAD
| PROCESS_QUERY_INFORMATION
| PROCESS_VM_OPERATION
| PROCESS_VM_READ
| PROCESS_VM_WRITE;
#[repr(transparent)]
#[derive(Debug)]
pub struct Process(OwnedHandle);
impl AsRawHandle for Process {
fn as_raw_handle(&self) -> HANDLE {
self.0.as_raw_handle()
}
}
impl IntoRawHandle for Process {
fn into_raw_handle(self) -> RawHandle {
self.0.into_raw_handle()
}
}
impl FromRawHandle for Process {
unsafe fn from_raw_handle(handle: HANDLE) -> Self {
Self(unsafe { OwnedHandle::from_raw_handle(handle) })
}
}
impl AsHandle for Process {
fn as_handle(&self) -> BorrowedHandle<'_> {
self.0.as_handle()
}
}
impl From<Child> for Process {
fn from(child: Child) -> Self {
Self::from_child(child)
}
}
impl PartialEq for Process {
fn eq(&self, other: &Self) -> bool {
self.get_ref() == other.get_ref()
}
}
impl Eq for Process {}
impl Hash for Process {
fn hash<H: Hasher>(&self, state: &mut H) {
self.get_ref().hash(state);
}
}
impl Deref for Process {
type Target = ProcessRef<'static>;
fn deref(&self) -> &Self::Target {
unsafe {
&*(&self.0 as *const OwnedHandle as *const BorrowedHandle<'_> as *const ProcessRef<'_>)
}
}
}
impl DerefMut for Process {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe {
&mut *(&mut self.0 as *mut OwnedHandle as *mut BorrowedHandle<'_>
as *mut ProcessRef<'_>)
}
}
}
impl AsRef<ProcessRef<'static>> for Process {
fn as_ref(&self) -> &ProcessRef<'static> {
self.deref()
}
}
impl AsMut<ProcessRef<'static>> for Process {
fn as_mut(&mut self) -> &mut ProcessRef<'static> {
self.deref_mut()
}
}
impl Process {
pub fn from_pid(pid: u32) -> Result<Self, io::Error> {
let handle = unsafe {
OpenProcess(
PROCESS_INJECTION_ACCESS,
FALSE,
pid,
)
};
if handle.is_null() {
return Err(io::Error::last_os_error());
}
Ok(unsafe { Self::from_raw_handle(handle) })
}
#[must_use]
pub fn all() -> Vec<Self> {
let mut system = sysinfo::System::new();
system.refresh_processes();
system
.processes()
.values()
.map(|process| process.pid())
.filter_map(|pid| Process::from_pid(pid.as_u32()).ok())
.collect()
}
#[must_use]
pub fn find_all_by_name(name: impl AsRef<str>) -> Vec<Self> {
let mut system = sysinfo::System::new();
system.refresh_processes();
system
.processes()
.values()
.filter(move |process| process.name().contains(name.as_ref()))
.map(|process| process.pid())
.filter_map(|pid| Process::from_pid(pid.as_u32()).ok())
.collect()
}
#[must_use]
pub fn find_first_by_name(name: impl AsRef<str>) -> Option<Self> {
let mut system = sysinfo::System::new();
system.refresh_processes();
system
.processes()
.values()
.filter(move |process| process.name().contains(name.as_ref()))
.map(|process| process.pid())
.find_map(|pid| Process::from_pid(pid.as_u32()).ok())
}
#[must_use]
pub fn from_child(child: Child) -> Self {
unsafe { Self::from_raw_handle(child.into_raw_handle()) }
}
#[must_use]
pub fn get_ref(&'_ self) -> ProcessRef<'_> {
unsafe { ProcessRef::borrow_from_handle(self.as_handle()) }
}
pub fn try_clone(&self) -> Result<Self, io::Error> {
ProcessRef::promote_to_owned(&self.get_ref())
}
#[allow(clippy::must_use_candidate)]
pub fn leak(self) -> ProcessRef<'static> {
unsafe {
ProcessRef::borrow_from_handle(BorrowedHandle::borrow_raw_handle(
self.into_raw_handle(),
))
}
}
#[must_use]
pub const fn kill_on_drop(self) -> ProcessKillGuard {
ProcessKillGuard(self)
}
}
#[derive(Debug, shrinkwraprs::Shrinkwrap)]
#[shrinkwrap(mutable)]
pub struct ProcessKillGuard(pub Process);
impl Drop for ProcessKillGuard {
fn drop(&mut self) {
let _ = self.0.kill();
}
}