use self::{
module::ModuleEntryIter,
thread::{ThreadIdIter, ThreadIter},
};
pub use self::{
module::{Module, ModuleEntry, ModuleInfo},
thread::{PriorityLevel, Thread},
};
use crate::{Error, Handle, WinResult};
use bitflags::bitflags;
use std::{
ffi::{OsStr, OsString},
mem,
ops::Deref,
os::windows::{
io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle},
prelude::*,
},
path::PathBuf,
};
use widestring::WideCString;
use winapi::{
shared::{
basetsd::DWORD_PTR,
minwindef::{DWORD, HMODULE, MAX_PATH},
},
um::{
handleapi::INVALID_HANDLE_VALUE,
libloaderapi::GetModuleHandleW,
processthreadsapi::{
GetCurrentProcess,
GetExitCodeProcess,
GetPriorityClass,
GetProcessId,
OpenProcess,
SetPriorityClass,
TerminateProcess,
},
psapi::{EnumProcessModulesEx, LIST_MODULES_ALL},
tlhelp32::{
CreateToolhelp32Snapshot,
Process32Next,
PROCESSENTRY32,
TH32CS_SNAPMODULE,
TH32CS_SNAPMODULE32,
TH32CS_SNAPPROCESS,
TH32CS_SNAPTHREAD,
},
winbase::{
GetProcessAffinityMask,
QueryFullProcessImageNameW,
SetProcessAffinityMask,
ABOVE_NORMAL_PRIORITY_CLASS,
BELOW_NORMAL_PRIORITY_CLASS,
HIGH_PRIORITY_CLASS,
IDLE_PRIORITY_CLASS,
NORMAL_PRIORITY_CLASS,
PROCESS_MODE_BACKGROUND_BEGIN,
PROCESS_MODE_BACKGROUND_END,
REALTIME_PRIORITY_CLASS,
},
winnt::{self, PROCESS_ALL_ACCESS, WCHAR},
},
};
mod module;
mod thread;
#[derive(Debug)]
pub struct Process {
handle: Handle,
}
impl Process {
pub fn from_id(id: u32) -> WinResult<Process> {
unsafe {
let handle = OpenProcess(PROCESS_ALL_ACCESS, 0, id);
if handle.is_null() {
Err(Error::last_os_error())
} else {
Ok(Process {
handle: Handle::new(handle),
})
}
}
}
pub fn from_id_with_access(id: u32, access: Access) -> WinResult<Process> {
unsafe {
let handle = OpenProcess(access.bits, 0, id);
if handle.is_null() {
Err(Error::last_os_error())
} else {
Ok(Process {
handle: Handle::new(handle),
})
}
}
}
pub fn from_name(name: &str) -> WinResult<Process> {
Process::all()?
.find(|p| p.name().map(|n| n == name).unwrap_or(false))
.ok_or(Error::NoProcess(name.to_string()))
}
pub fn from_name_with_access(name: &str, access: Access) -> WinResult<Process> {
Process::all_with_access(access)?
.find(|p| p.name().map(|n| n == name).unwrap_or(false))
.ok_or(Error::NoProcess(name.to_string()))
}
pub fn from_handle(handle: Handle) -> Process {
Process { handle }
}
pub fn current() -> Process {
unsafe { Process::from_handle(Handle::from_raw_handle(GetCurrentProcess() as RawHandle)) }
}
pub fn handle(&self) -> &Handle {
&self.handle
}
pub fn all() -> WinResult<impl Iterator<Item = Process>> {
unsafe {
let snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if snap == INVALID_HANDLE_VALUE {
Err(Error::last_os_error())
} else {
Ok(ProcessIter {
snapshot: Handle::new(snap),
access: Access::PROCESS_ALL_ACCESS,
}
.filter_map(Result::ok))
}
}
}
pub fn all_with_access(access: Access) -> WinResult<impl Iterator<Item = Process>> {
unsafe {
let snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if snap == INVALID_HANDLE_VALUE {
Err(Error::last_os_error())
} else {
Ok(ProcessIter {
snapshot: Handle::new(snap),
access,
}
.filter_map(Result::ok))
}
}
}
pub fn id(&self) -> u32 {
unsafe { GetProcessId(self.handle.as_raw_handle() as winnt::HANDLE) }
}
pub fn is_running(&self) -> bool {
unsafe {
let mut status = 0;
GetExitCodeProcess(self.handle.as_raw_handle() as winnt::HANDLE, &mut status);
status == 259
}
}
pub fn path(&self) -> WinResult<PathBuf> {
unsafe {
let mut size = MAX_PATH as u32;
let mut buffer: [WCHAR; MAX_PATH] = mem::zeroed();
let ret = QueryFullProcessImageNameW(
self.handle.as_raw_handle() as winnt::HANDLE,
0,
buffer.as_mut_ptr(),
&mut size,
);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(OsString::from_wide(&buffer[0..size as usize]).into())
}
}
}
pub fn name(&self) -> WinResult<String> {
Ok(self
.path()?
.file_name()
.unwrap()
.to_string_lossy()
.into_owned())
}
pub fn priority(&self) -> WinResult<PriorityClass> {
unsafe {
let ret = GetPriorityClass(self.handle.as_raw_handle() as winnt::HANDLE);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(PriorityClass::from_code(ret))
}
}
}
pub fn set_priority(&mut self, priority: PriorityClass) -> WinResult {
unsafe {
let ret = SetPriorityClass(
self.handle.as_raw_handle() as winnt::HANDLE,
priority.as_code(),
);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
}
pub fn start_background_mode(&mut self) -> WinResult {
unsafe {
let ret = SetPriorityClass(
self.handle.as_raw_handle() as winnt::HANDLE,
PROCESS_MODE_BACKGROUND_BEGIN,
);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
}
pub fn end_background_mode(&mut self) -> WinResult {
unsafe {
let ret = SetPriorityClass(
self.handle.as_raw_handle() as winnt::HANDLE,
PROCESS_MODE_BACKGROUND_END,
);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
}
pub fn terminate(&mut self, exit_code: u32) -> WinResult {
unsafe {
let ret = TerminateProcess(self.handle.as_raw_handle() as winnt::HANDLE, exit_code);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
}
pub fn affinity_mask(&self) -> WinResult<usize> {
unsafe {
let mut process_mask: DWORD_PTR = 0;
let mut system_mask: DWORD_PTR = 0;
let ret = GetProcessAffinityMask(
self.handle.as_raw_handle() as winnt::HANDLE,
&mut process_mask,
&mut system_mask,
);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(process_mask as usize)
}
}
}
pub fn set_affinity_mask(&mut self, mask: u32) -> WinResult {
unsafe {
let ret = SetProcessAffinityMask(self.handle.as_raw_handle() as winnt::HANDLE, mask);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
}
pub fn threads<'a>(&'a self) -> WinResult<impl Iterator<Item = Thread> + 'a> {
unsafe {
let snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if snap == INVALID_HANDLE_VALUE {
Err(Error::last_os_error())
} else {
Ok(ThreadIter {
process: &self,
snapshot: Handle::new(snap),
}
.filter_map(Result::ok))
}
}
}
pub fn thread_ids<'a>(&'a self) -> WinResult<impl Iterator<Item = u32> + 'a> {
unsafe {
let snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if snap == INVALID_HANDLE_VALUE {
Err(Error::last_os_error())
} else {
Ok(ThreadIdIter {
process: &self,
snapshot: Handle::new(snap),
})
}
}
}
pub fn module<N: AsRef<OsStr>>(&self, name: N) -> WinResult<Module> {
unsafe {
let name = WideCString::from_os_str(name).map_err(|e| Error::NulErrorW {
pos: e.nul_position(),
data: e.into_vec(),
})?;
let ret = GetModuleHandleW(name.as_ptr());
if ret.is_null() {
Err(Error::last_os_error())
} else {
Ok(Module {
handle: ret,
process: self,
})
}
}
}
pub fn module_list(&self) -> WinResult<Vec<Module>> {
unsafe {
let mut mod_handles = Vec::new();
let mut reserved = 0;
let mut needed = 0;
{
let enum_mods = |mod_handles: &mut [HMODULE], needed| {
let res = EnumProcessModulesEx(
self.as_raw_handle() as winnt::HANDLE,
mod_handles.as_mut_ptr(),
mem::size_of_val(&mod_handles[..]) as _,
needed,
LIST_MODULES_ALL,
);
if res == 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
};
loop {
enum_mods(&mut mod_handles, &mut needed)?;
if needed <= reserved {
break;
}
reserved = needed;
mod_handles.resize(needed as usize, mem::zeroed());
}
}
let modules = mod_handles[..needed as usize / mem::size_of::<HMODULE>()]
.iter()
.map(|&handle| Module {
handle,
process: self,
})
.collect();
Ok(modules)
}
}
pub fn module_entries<'a>(&'a self) -> WinResult<impl Iterator<Item = ModuleEntry> + 'a> {
unsafe {
let snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, self.id());
if snap == INVALID_HANDLE_VALUE {
Err(Error::last_os_error())
} else {
Ok(ModuleEntryIter {
process: &self,
snapshot: Handle::new(snap),
})
}
}
}
}
impl AsRawHandle for Process {
fn as_raw_handle(&self) -> RawHandle {
self.handle.as_raw_handle()
}
}
impl Deref for Process {
type Target = winnt::HANDLE;
fn deref(&self) -> &winnt::HANDLE {
&*self.handle
}
}
impl FromRawHandle for Process {
unsafe fn from_raw_handle(handle: RawHandle) -> Process {
Process {
handle: Handle::new(handle as winnt::HANDLE),
}
}
}
impl IntoRawHandle for Process {
fn into_raw_handle(self) -> RawHandle {
self.handle.into_raw_handle()
}
}
#[derive(Debug)]
struct ProcessIter {
snapshot: Handle,
access: Access,
}
impl Iterator for ProcessIter {
type Item = WinResult<Process>;
fn next(&mut self) -> Option<WinResult<Process>> {
unsafe {
let mut entry: PROCESSENTRY32 = mem::zeroed();
entry.dwSize = mem::size_of::<PROCESSENTRY32>() as DWORD;
let ret = Process32Next(self.snapshot.as_raw_handle() as winnt::HANDLE, &mut entry);
if ret == 0 {
None
} else {
Some(Process::from_id_with_access(
entry.th32ProcessID,
self.access,
))
}
}
}
}
bitflags! {
pub struct Access: u32 {
const DELETE = winnt::DELETE;
const READ_CONTROL = winnt::READ_CONTROL;
const WRITE_DAC = winnt::WRITE_DAC;
const WRITE_OWNER = winnt::WRITE_OWNER;
const SYNCHRONIZE = winnt::SYNCHRONIZE;
const STANDARD_RIGHTS_REQUIRED = winnt::STANDARD_RIGHTS_REQUIRED;
const PROCESS_TERMINATE = winnt::PROCESS_TERMINATE;
const PROCESS_CREATE_THREAD = winnt::PROCESS_CREATE_THREAD;
const PROCESS_SET_SESSIONID = winnt::PROCESS_SET_SESSIONID;
const PROCESS_VM_OPERATION = winnt::PROCESS_VM_OPERATION;
const PROCESS_VM_READ = winnt::PROCESS_VM_READ;
const PROCESS_VM_WRITE = winnt::PROCESS_VM_WRITE;
const PROCESS_DUP_HANDLE = winnt::PROCESS_DUP_HANDLE;
const PROCESS_CREATE_PROCESS = winnt::PROCESS_CREATE_PROCESS;
const PROCESS_SET_QUOTA = winnt::PROCESS_SET_QUOTA;
const PROCESS_SET_INFORMATION = winnt::PROCESS_SET_INFORMATION;
const PROCESS_QUERY_INFORMATION = winnt::PROCESS_QUERY_INFORMATION;
const PROCESS_SUSPEND_RESUME = winnt::PROCESS_SUSPEND_RESUME;
const PROCESS_QUERY_LIMITED_INFORMATION = winnt::PROCESS_QUERY_LIMITED_INFORMATION;
const PROCESS_SET_LIMITED_INFORMATION = winnt::PROCESS_SET_LIMITED_INFORMATION;
const PROCESS_ALL_ACCESS = Self::STANDARD_RIGHTS_REQUIRED.bits | Self::SYNCHRONIZE.bits | 0xffff;
}
}
impl Default for Access {
fn default() -> Access {
Access::PROCESS_ALL_ACCESS
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum PriorityClass {
Idle,
BelowNormal,
Normal,
AboveNormal,
High,
Realtime,
}
impl PriorityClass {
fn from_code(code: DWORD) -> PriorityClass {
match code {
IDLE_PRIORITY_CLASS => PriorityClass::Idle,
BELOW_NORMAL_PRIORITY_CLASS => PriorityClass::BelowNormal,
NORMAL_PRIORITY_CLASS => PriorityClass::Normal,
ABOVE_NORMAL_PRIORITY_CLASS => PriorityClass::AboveNormal,
HIGH_PRIORITY_CLASS => PriorityClass::High,
REALTIME_PRIORITY_CLASS => PriorityClass::Realtime,
_ => panic!("Unexpected priority code: {}", code),
}
}
fn as_code(&self) -> DWORD {
match self {
PriorityClass::Idle => IDLE_PRIORITY_CLASS,
PriorityClass::BelowNormal => BELOW_NORMAL_PRIORITY_CLASS,
PriorityClass::Normal => NORMAL_PRIORITY_CLASS,
PriorityClass::AboveNormal => ABOVE_NORMAL_PRIORITY_CLASS,
PriorityClass::High => HIGH_PRIORITY_CLASS,
PriorityClass::Realtime => REALTIME_PRIORITY_CLASS,
}
}
}
impl Default for PriorityClass {
fn default() -> PriorityClass {
PriorityClass::Normal
}
}