use std::{
borrow::Cow,
cmp,
convert::TryInto,
hash::{Hash, Hasher},
mem::{self, MaybeUninit},
os::windows::{
prelude::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle},
raw::HANDLE,
},
path::Path,
};
use rust_win32error::Win32Error;
use winapi::{
shared::minwindef::{FALSE, HMODULE},
um::{
handleapi::DuplicateHandle,
processthreadsapi::{GetCurrentProcess, TerminateProcess},
psapi::{EnumProcessModulesEx, LIST_MODULES_ALL},
winnt::DUPLICATE_SAME_ACCESS,
wow64apiset::IsWow64Process,
},
};
use crate::{
utils::{ArrayOrVecSlice, UninitArrayBuf},
ModuleHandle, Process, ProcessHandle, ProcessModule,
};
#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
pub struct ProcessRef<'a>(BorrowedHandle<'a>);
impl AsRawHandle for ProcessRef<'_> {
fn as_raw_handle(&self) -> HANDLE {
self.0.as_raw_handle()
}
}
impl AsHandle for ProcessRef<'_> {
fn as_handle(&self) -> BorrowedHandle<'_> {
self.0.as_handle()
}
}
impl PartialEq for ProcessRef<'_> {
fn eq(&self, other: &Self) -> bool {
self.as_raw_handle() == other.as_raw_handle()
}
}
impl Eq for ProcessRef<'_> {}
impl Hash for ProcessRef<'_> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_raw_handle().hash(state)
}
}
impl<'a> From<&'a Process> for ProcessRef<'a> {
fn from(process: &'a Process) -> Self {
process.get_ref()
}
}
impl<'a> ProcessRef<'a> {
pub unsafe fn borrow_from_handle(handle: BorrowedHandle<'a>) -> Self {
Self(handle)
}
#[must_use]
pub fn current_handle() -> BorrowedHandle<'static> {
unsafe { BorrowedHandle::borrow_raw_handle(Self::raw_current_handle()) }
}
#[must_use]
pub fn raw_current_handle() -> ProcessHandle {
unsafe { GetCurrentProcess() }
}
#[must_use]
pub fn current() -> Self {
Self(Self::current_handle())
}
#[must_use]
pub fn is_current(&self) -> bool {
self.handle() == ProcessRef::raw_current_handle()
}
#[must_use]
pub fn handle(&self) -> ProcessHandle {
self.as_raw_handle()
}
pub fn promote_to_owned(&self) -> Result<Process, Win32Error> {
let raw_handle = self.as_raw_handle();
let process = unsafe { GetCurrentProcess() };
let mut new_handle = MaybeUninit::uninit();
let result = unsafe {
DuplicateHandle(
process,
raw_handle,
process,
new_handle.as_mut_ptr(),
0,
FALSE,
DUPLICATE_SAME_ACCESS,
)
};
if result == 0 {
return Err(Win32Error::new());
}
Ok(unsafe { Process::from_raw_handle(new_handle.assume_init()) })
}
pub fn get_module_handles(&self) -> Result<impl AsRef<[ModuleHandle]>, Win32Error> {
let mut module_buf = UninitArrayBuf::<ModuleHandle, 1024>::new();
let mut module_buf_byte_size = mem::size_of::<HMODULE>() * module_buf.len();
let mut bytes_needed_target = MaybeUninit::uninit();
let result = unsafe {
EnumProcessModulesEx(
self.handle(),
module_buf.as_mut_ptr(),
module_buf_byte_size.try_into().unwrap(),
bytes_needed_target.as_mut_ptr(),
LIST_MODULES_ALL,
)
};
if result == 0 {
return Err(Win32Error::new());
}
let mut bytes_needed = unsafe { bytes_needed_target.assume_init() } as usize;
let modules = if bytes_needed <= module_buf_byte_size {
let module_buf_len = bytes_needed / mem::size_of::<HMODULE>();
let module_buf_init = unsafe { module_buf.assume_init_all() };
ArrayOrVecSlice::from_array(module_buf_init, 0..module_buf_len)
} else {
let mut module_buf_vec = Vec::new();
loop {
module_buf_byte_size = cmp::max(bytes_needed, module_buf_byte_size * 2);
let mut module_buf_len = module_buf_byte_size / mem::size_of::<HMODULE>();
module_buf_vec.resize_with(module_buf_len, MaybeUninit::uninit);
bytes_needed_target = MaybeUninit::uninit();
let result = unsafe {
EnumProcessModulesEx(
self.handle(),
module_buf_vec[0].as_mut_ptr(),
module_buf_byte_size.try_into().unwrap(),
bytes_needed_target.as_mut_ptr(),
LIST_MODULES_ALL,
)
};
if result == 0 {
return Err(Win32Error::new());
}
bytes_needed = unsafe { bytes_needed_target.assume_init() } as usize;
if bytes_needed <= module_buf_byte_size {
module_buf_len = bytes_needed / mem::size_of::<HMODULE>();
let module_buf_vec = unsafe {
mem::transmute::<Vec<MaybeUninit<HMODULE>>, Vec<ModuleHandle>>(
module_buf_vec,
)
};
break ArrayOrVecSlice::from_vec(module_buf_vec, 0..module_buf_len);
}
}
};
Ok(modules)
}
pub fn find_module_by_name(
&self,
module_name: impl AsRef<Path>,
) -> Result<Option<ProcessModule<'a>>, Win32Error> {
let target_module_name = module_name.as_ref();
let target_module_name = if target_module_name.extension().is_some() {
Cow::Owned(target_module_name.with_extension("dll").into_os_string())
} else {
Cow::Borrowed(target_module_name.as_os_str())
};
let modules = self.get_module_handles()?;
for &module_handle in modules.as_ref() {
let module = unsafe { ProcessModule::new(module_handle, *self) };
let module_name = module.get_base_name()?;
if module_name.eq_ignore_ascii_case(&target_module_name) {
return Ok(Some(module));
}
}
Ok(None)
}
pub fn find_module_by_path(
&self,
module_path: impl AsRef<Path>,
) -> Result<Option<ProcessModule<'a>>, Win32Error> {
let target_module_path = module_path.as_ref();
let target_module_path = if target_module_path.extension().is_some() {
Cow::Owned(target_module_path.with_extension("dll").into_os_string())
} else {
Cow::Borrowed(target_module_path.as_os_str())
};
let modules = self.get_module_handles()?;
for &module_handle in modules.as_ref() {
let module = unsafe { ProcessModule::new(module_handle, *self) };
let module_path = module.get_path()?.into_os_string();
if module_path.eq_ignore_ascii_case(&target_module_path) {
return Ok(Some(module));
}
}
Ok(None)
}
pub fn is_wow64(&self) -> Result<bool, Win32Error> {
let mut is_wow64 = MaybeUninit::uninit();
let result = unsafe { IsWow64Process(self.handle(), is_wow64.as_mut_ptr()) };
if result == 0 {
return Err(Win32Error::new());
}
Ok(unsafe { is_wow64.assume_init() } != FALSE)
}
pub fn kill(self) -> Result<(), Win32Error> {
self.kill_with_exit_code(1)
}
pub fn kill_with_exit_code(self, exit_code: u32) -> Result<(), Win32Error> {
let result = unsafe { TerminateProcess(self.handle(), exit_code) };
if result == 0 {
return Err(Win32Error::new());
}
Ok(())
}
}