use bitflags::bitflags;
use std::{ffi::OsString, io, mem, os::windows::io::AsRawHandle};
use windows_sys::Win32::{
Foundation::{CloseHandle, ERROR_NO_MORE_FILES, HANDLE, INVALID_HANDLE_VALUE},
System::Diagnostics::ToolHelp::{
CreateToolhelp32Snapshot, MODULEENTRY32W, Module32FirstW, Module32NextW, PROCESSENTRY32W,
Process32FirstW, Process32NextW, TH32CS_SNAPMODULE, TH32CS_SNAPMODULE32,
TH32CS_SNAPPROCESS,
},
};
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SnapshotFlags: u32 {
const MODULE = TH32CS_SNAPMODULE;
const MODULE32 = TH32CS_SNAPMODULE32;
const PROCESS = TH32CS_SNAPPROCESS;
}
}
pub fn create_toolhelp32_snapshot(
flags: SnapshotFlags,
process_id: u32,
) -> io::Result<ProcessSnapshot> {
ProcessSnapshot::new(flags, process_id)
}
pub struct ProcessSnapshot {
handle: HANDLE,
}
impl ProcessSnapshot {
pub fn new_processes() -> io::Result<ProcessSnapshot> {
Self::new(SnapshotFlags::PROCESS, 0)
}
pub fn new_modules(process_id: u32) -> io::Result<ProcessSnapshot> {
Self::new(SnapshotFlags::MODULE, process_id)
}
pub fn new_modules32(process_id: u32) -> io::Result<ProcessSnapshot> {
Self::new(SnapshotFlags::MODULE32, process_id)
}
fn new(flags: SnapshotFlags, process_id: u32) -> io::Result<ProcessSnapshot> {
let snap = unsafe { CreateToolhelp32Snapshot(flags.bits(), process_id) };
if snap == INVALID_HANDLE_VALUE {
Err(io::Error::last_os_error())
} else {
Ok(ProcessSnapshot { handle: snap })
}
}
pub fn as_raw(&self) -> HANDLE {
self.handle
}
pub fn modules(&self) -> ProcessSnapshotModules<'_> {
let entry = MODULEENTRY32W {
dwSize: mem::size_of::<MODULEENTRY32W>() as u32,
..Default::default()
};
ProcessSnapshotModules {
snapshot: self,
iter_started: false,
temp_entry: entry,
}
}
pub fn processes(&self) -> ProcessSnapshotEntries<'_> {
let entry = PROCESSENTRY32W {
dwSize: mem::size_of::<PROCESSENTRY32W>() as u32,
..Default::default()
};
ProcessSnapshotEntries {
snapshot: self,
iter_started: false,
temp_entry: entry,
}
}
}
impl AsRawHandle for ProcessSnapshot {
fn as_raw_handle(&self) -> std::os::windows::prelude::RawHandle {
self.handle
}
}
impl Drop for ProcessSnapshot {
fn drop(&mut self) {
unsafe {
CloseHandle(self.handle);
}
}
}
pub struct ModuleEntry {
entry: MODULEENTRY32W,
}
impl ModuleEntry {
pub fn name(&self) -> OsString {
let name_ptr = &raw const self.entry.szModule[0];
unsafe { crate::util::osstring_from_wide(name_ptr) }
}
pub fn base_address(&self) -> *const u8 {
self.entry.modBaseAddr
}
pub fn size(&self) -> usize {
usize::try_from(self.entry.modBaseSize).unwrap()
}
pub fn as_raw(&self) -> &MODULEENTRY32W {
&self.entry
}
}
pub struct ProcessSnapshotModules<'a> {
snapshot: &'a ProcessSnapshot,
iter_started: bool,
temp_entry: MODULEENTRY32W,
}
impl Iterator for ProcessSnapshotModules<'_> {
type Item = io::Result<ModuleEntry>;
fn next(&mut self) -> Option<io::Result<ModuleEntry>> {
if self.iter_started {
if unsafe { Module32NextW(self.snapshot.as_raw(), &mut self.temp_entry) } == 0 {
let last_error = io::Error::last_os_error();
return if last_error.raw_os_error().unwrap() as u32 == ERROR_NO_MORE_FILES {
None
} else {
Some(Err(last_error))
};
}
} else {
if unsafe { Module32FirstW(self.snapshot.as_raw(), &mut self.temp_entry) } == 0 {
return Some(Err(io::Error::last_os_error()));
}
self.iter_started = true;
}
Some(Ok(ModuleEntry {
entry: self.temp_entry,
}))
}
}
pub struct ProcessEntry {
entry: PROCESSENTRY32W,
}
impl ProcessEntry {
pub fn pid(&self) -> u32 {
self.entry.th32ProcessID
}
pub fn parent_pid(&self) -> u32 {
self.entry.th32ParentProcessID
}
pub fn as_raw(&self) -> &PROCESSENTRY32W {
&self.entry
}
}
pub struct ProcessSnapshotEntries<'a> {
snapshot: &'a ProcessSnapshot,
iter_started: bool,
temp_entry: PROCESSENTRY32W,
}
impl Iterator for ProcessSnapshotEntries<'_> {
type Item = io::Result<ProcessEntry>;
fn next(&mut self) -> Option<io::Result<ProcessEntry>> {
if self.iter_started {
if unsafe { Process32NextW(self.snapshot.as_raw(), &mut self.temp_entry) } == 0 {
let last_error = io::Error::last_os_error();
return if last_error.raw_os_error().unwrap() as u32 == ERROR_NO_MORE_FILES {
None
} else {
Some(Err(last_error))
};
}
} else {
if unsafe { Process32FirstW(self.snapshot.as_raw(), &mut self.temp_entry) } == 0 {
return Some(Err(io::Error::last_os_error()));
}
self.iter_started = true;
}
Some(Ok(ProcessEntry {
entry: self.temp_entry,
}))
}
}