Preface
Official Windows API DocumentationProvides C++ call examples. I want to try to implement it with Rust recently. This series of blogs records the implementation process.
rely
Rust calls to Windows API requires the introduction of dependencieswinapi
,existAdd dependencies to
winapi = "0.3.9"
Calling different API sets requires the use of corresponding functionsfeatures
A good way to judge is to add the feature in which header file you see in Microsoft's official documentation, for example, this article needs to be used and Then add these two features to it
winapi = { version = "0.3.9", features = ["tlhelp32", "processthreadsapi"] }
accomplish
Approximate steps:
- Create a process snapshot and get the snapshot handle
- Iterate through the processes in the snapshot (implemented in an iterator) to obtain the data of each process
- Release the snapshot handle
Create a snapshot handle
Create a process snapshot to useCreateToolhelp32Snapshot Method, it's inDefined in the header file.
use winapi::um::tlhelp32::{CreateToolhelp32Snapshot, TH32CS_SNAPPROCESS}; use winapi::um::handleapi::INVALID_HANDLE_VALUE; use winapi::um::errhandlingapi::GetLastError; /// Save a process snapshot and return a process information iterator `ProcessInformationIterator`pub fn list() -> Result<ProcessInformationIterator, String> { let process_snapshot: HANDLE = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }; if process_snapshot == INVALID_HANDLE_VALUE || process_snapshot.is_null() { unsafe { return Err(format!("Cannot list processes, code: {}", GetLastError())); } } Ok(ProcessInformationIterator::new(process_snapshot)) }
In the codeProcessInfomationIteratorIt is custom, and for clearer structure, iterator mode is used here to read.
If the process snapshot is saved, the returned handle will be an invalid value (there are two conditions or relationships used here to determine whether it is invalid. In fact, you can use either of them. They both represent an "empty" memory or an "empty" pointer).GetLastErrorThe method can obtain the error code, and the corresponding meaning of the error code is shown inSystem error code description, it can also be parsed into readable text through the API. This article will be introduced later. Here we will use code to simply express it.
Implement an iterator
I won't go into details about the iterator pattern implementation method in Rust. You only need to know that at least you need to implement an iterator.An iterative element ItemandAn iterator that implements the Iterator featureThat's it.
Iterative element Item
let vec = vec![1, 2] for item in vec { ... }
The item in the above code is the specific element in the iterator. Because there is a lot of process information, a structure is used to store it here.
use winapi::um::tlhelp32::PROCESSENTRY32; pub struct ProcessInformation { inner: PROCESSENTRY32, }
Here, the process's data is not directly parsed and then stored in the structure, but directlyPROCESSENTRY32The structure is made into a wrapper. Here, in order to save unnecessary calculations, the PROCESSENTRY32 directly read from the handle is not all information that is directly readable by Rust and is parsed when needed. It is more convenient to read data through the getter method to expand later. The following is the specific method implementation of ProcessInformation.
use winapi::um::processthreadsapi::{GetPriorityClass, OpenProcess}; use winapi::um::errhandlingapi::GetLastError; use winapi::um::tlhelp32::PROCESSENTRY32; use winapi::um::winnt::{HANDLE, PROCESS_ALL_ACCESS}; pub(crate) fn char_arr_to_string(chars: &[i8]) -> String { chars.into_iter().map(|&c| c as u8 as char).collect() } impl ProcessInformation { pub(crate) fn new(entry: PROCESSENTRY32) -> ProcessInformation { ProcessInformation { inner: entry } } /// Get the process ID pub fn get_pid(&self) -> u32 { .th32ProcessID as u32 } /// Get the process name pub fn get_name(&self) -> String { char_arr_to_string(&) } /// Get the parent process ID pub fn get_parent_pid(&self) -> u32 { .th32ParentProcessID as u32 } /// Get the number of threads pub fn get_thread_count(&self) -> usize { as usize } /// Get the basic priority value pub fn get_priority_base(&self) -> i64 { as i64 } /// Get the priority category. If the calling process does not have specified permissions, it may fail to obtain. Return `None` if it fails. pub fn get_priority_class(&self) -> Option<i32> { let mut priority_class = None; unsafe { let handle = OpenProcess(PROCESS_ALL_ACCESS, 0, .th32ProcessID); if !handle.is_null() { let class = GetPriorityClass(handle); CloseHandle(handle); priority_class = Some(class as i32); } } priority_class } }
Iterator implementation
The iterator needs to save some iterative traversal states, so in addition to the snapshot handle saved earlier, the iterative index and the state of releasing the handle should also be stored.Iterators are irreversible。
use winapi::um::winnt::HANDLE; pub struct ProcessInformationIterator { process_snapshot: HANDLE, index: usize, finished: bool, } impl ProcessInformationIterator { pub(crate) fn new(process_snapshot: HANDLE) -> ProcessInformationIterator { ProcessInformationIterator { process_snapshot, index: 0, finished: false, } } }
Then there is the specific implementation of the iterator
use winapi::um::winnt::HANDLE; use winapi::um::tlhelp32::{Process32First, Process32Next, PROCESSENTRY32}; use winapi::um::handleapi::CloseHandle; impl Iterator for ProcessInformationIterator { type Item = ProcessInformation; fn next(&mut self) -> Option<Self::Item> { if { return None; } += 1; let mut entry: PROCESSENTRY32 = unsafe { std::mem::zeroed() }; = size_of::<PROCESSENTRY32>() as u32; // Read the first process in the snapshot let res = unsafe { if == 1 { Process32First(self.process_snapshot, &mut entry) } else { Process32Next(self.process_snapshot, &mut entry) } }; if res == 0 { unsafe { CloseHandle(self.process_snapshot); } = true; return None; } Some(ProcessInformation::new(entry)) } }
There are a few points to explain in the above code:
- When initializing the entry, you need to first reach a memory space with all 0 values, and it is not allocated as a Rust reference space. Here, the unsafe method is used.
std::mem::zeroed()
- Before reading process Entry, you need to specify the memory length.
size_of::<PROCESSENTRY32>()
to obtain and assign value to - The first element needs to be called during traversal
Process32First
Read, subsequent useProcess32Next
Read - Remember to close the snapshot script when traversing
CloseHandle
interface
Special case handling: If the user has not finished iteration, the above code implementation may cause the snapshot handle to not be released, so it is also necessary to implement a Drop feature for the iterator, and release the snapshot handle when the iterator is released.
impl Drop for ProcessInformationIterator { fn drop(&mut self) { if { return; } // Release the snapshot handle. unsafe { CloseHandle(self.process_snapshot); } = true; } }
Code summary
I put it in custom when I wrote itutils::process::win
The specific reference path is adjusted according to your own situation
document
use crate::utils::process::win::process_information::ProcessInformationIterator; use winapi::um::tlhelp32::{CreateToolhelp32Snapshot, TH32CS_SNAPPROCESS}; use winapi::um::errhandlingapi::GetLastError; use winapi::um::handleapi::INVALID_HANDLE_VALUE; use winapi::um::winnt::HANDLE; pub mod process_information; pub fn list() -> Result<ProcessInformationIterator, String> { let process_snapshot: HANDLE = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }; if process_snapshot == INVALID_HANDLE_VALUE || process_snapshot.is_null() { unsafe { return Err(format!("Cannot list processes, code: {}", GetLastError())); } } Ok(ProcessInformationIterator::new(process_snapshot)) } pub(crate) fn char_arr_to_string(chars: &[i8]) -> String { chars.into_iter().map(|&c| c as u8 as char).collect() }
process_information.rs
document
use crate::utils::process::win::char_arr_to_string; use crate::utils::process::win::process_module::ProcessModuleIterator; use winapi::um::errhandlingapi::GetLastError; use winapi::um::handleapi::CloseHandle; use winapi::um::processthreadsapi::{GetPriorityClass, OpenProcess}; use winapi::um::tlhelp32::{Process32First, Process32Next, PROCESSENTRY32}; use winapi::um::winnt::{HANDLE, PROCESS_ALL_ACCESS}; //// Rust packaging implementation of [PROCESSENTRY32](/zh-cn/windows/win32/api/tlhelp32/ns-tlhelp32-processentry32)pub struct ProcessInformation { inner: PROCESSENTRY32, } impl ProcessInformation { pub(crate) fn new(entry: PROCESSENTRY32) -> ProcessInformation { ProcessInformation { inner: entry } } /// Get the process ID pub fn get_pid(&self) -> u32 { .th32ProcessID as u32 } /// Get the process name pub fn get_name(&self) -> String { char_arr_to_string(&) } /// Get the parent process ID pub fn get_parent_pid(&self) -> u32 { .th32ParentProcessID as u32 } /// Get the number of threads pub fn get_thread_count(&self) -> usize { as usize } /// Get the basic priority value pub fn get_priority_base(&self) -> i64 { as i64 } /// Get the priority category. If the calling process does not have specified permissions, it may fail to obtain. Return `None` if it fails. pub fn get_priority_class(&self) -> Option<i32> { let mut priority_class = None; unsafe { let handle = OpenProcess(PROCESS_ALL_ACCESS, 0, .th32ProcessID); if !handle.is_null() { let class = GetPriorityClass(handle); CloseHandle(handle); priority_class = Some(class as i32); } } priority_class } } pub struct ProcessInformationIterator { process_snapshot: HANDLE, index: usize, finished: bool, } impl ProcessInformationIterator { pub(crate) fn new(process_snapshot: HANDLE) -> ProcessInformationIterator { ProcessInformationIterator { process_snapshot, index: 0, finished: false, } } } impl Drop for ProcessInformationIterator { fn drop(&mut self) { if { return; } // Release the snapshot handle. unsafe { CloseHandle(self.process_snapshot); } = true; } } impl Iterator for ProcessInformationIterator { type Item = ProcessInformation; fn next(&mut self) -> Option<Self::Item> { if { return None; } += 1; let mut entry: PROCESSENTRY32 = unsafe { std::mem::zeroed() }; = size_of::<PROCESSENTRY32>() as u32; // Read the first process in the snapshot let res = unsafe { if == 1 { Process32First(self.process_snapshot, &mut entry) } else { Process32Next(self.process_snapshot, &mut entry) } }; if res == 0 { unsafe { CloseHandle(self.process_snapshot); } = true; return None; } Some(ProcessInformation::new(entry)) } }
test
Test code
#[test] pub fn test_list() { println!("PID\tName\tParent PID\tThreads\tPriority Base\tPriority Class"); let iter = list().unwrap(); for process in iter { let pid = process.get_pid(); let name = process.get_name(); let parent_pid = process.get_parent_pid(); let thread_count = process.get_thread_count(); let priority_base = process.get_priority_base(); let priority_class = process.get_priority_class(); println!("{}\t{}\t{}\t{}\t{}\t{:?}", pid, name, parent_pid, thread_count, priority_base, priority_class) } }
result
PID Name Parent PID Threads Priority Base Priority Class 0 [System Process] 0 6 0 None 4 System 0 236 8 None 64 Secure System 4 0 8 None 132 Registry 4 4 8 None 504 4 2 11 None 728 712 11 13 None 824 712 1 13 None 832 816 15 13 None ... 12624 12148 19 8 Some(32) 16352 12148 18 4 Some(64) 14904 12148 17 4 Some(64) 14672 892 2 8 None 11160 12148 20 4 Some(64) 18048 12148 19 4 Some(64) 5452 12148 14 4 Some(64) 14468 892 3 8 None 18060 12148 14 4 Some(64) 17748 688 8 8 Some(32) 16084 16648 27 8 Some(32) 9008 10644 6 8 Some(32) 15516 10644 4 8 Some(32) 11312 15516 4 8 Some(32)
This is the article about Rust calling the Windows API to obtain all running process information. For more related content on Rust calling the Windows API, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!