【Rust调用Windows API】获取正在运行的全部进程信息

前言

WIndows API官方文档 提供了C++的调用示例,最近想尝试用Rust去实现,本系列博客记录一下实现过程。

依赖

Rust调用Windows API需要引入依赖winapi,在Cargo.toml中添加依赖

winapi = "0.3.9"

调用不同的API集就需要使用相应的功能features,很好的一个判断方式是你在微软官方文档中看到的是在哪个头文件内,就添加哪个feature,例如本篇文章需要使用 tlhelp32.h 和 processthreadsapi.h 那么就将这俩feature添加进去

winapi = { version = "0.3.9", features = ["tlhelp32", "processthreadsapi"] }

实现

大致步骤

  1. 创建进程快照,拿到快照句柄
  2. 遍历快照中的进程(以迭代器的方式实现),得到每个进程的数据
  3. 释放快照句柄

创建快照句柄

创建进程快照需要用到 CreateToolhelp32Snapshot 方法,它在 tlhelp32.h 头文件中定义。

use winapi::um::tlhelp32::{CreateToolhelp32Snapshot, TH32CS_SNAPPROCESS};
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::errhandlingapi::GetLastError;/// 保存进程快照并返回进程信息迭代器 `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))
}

代码中的 ProcessInfomationIterator 是自定义的,为了结构更清晰,这里使用迭代器模式来读取。

如果保存进程快照失败,返回的句柄会是一个无效的值(这里用了两个条件或的关系去判断是否无效,其实任用其一都可以,他们都表示一个“空”内存或“空”指针),使用 GetLastError 方法可以获取错误代码,错误代码对应含义见系统错误代码说明,也可以通过API解析成可读文本,这个后面的文章再介绍,这里先用code简单表示一下。

实现迭代器

Rust中的迭代器模式实现方法这里就不多赘述,你只需要知道实现一个迭代器至少需要 一个迭代元素Item一个实现了Iterator特征的迭代器 就可以了。

迭代元素Item

let vec = vec![1, 2]
for item in vec {...
}

上面代码的item就是迭代器中具体的元素,因为进程信息有很多,这里就使用一个结构体来存

use winapi::um::tlhelp32::PROCESSENTRY32;pub struct ProcessInformation {inner: PROCESSENTRY32,
}

这里并没有直接将进程的数据解析之后再存入结构体,而是直接将 PROCESSENTRY32 结构体做一个包装,这里是为了节省不必要的计算,从句柄中直接读取出来的 PROCESSENTRY32 并不是所有信息都是Rust直接可读的,在需要时才解析,并且通过getter方法读取数据更方便以后拓展,下面是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}}/// 获取进程IDpub fn get_pid(&self) -> u32 {self.inner.th32ProcessID as u32}/// 获取进程名pub fn get_name(&self) -> String {char_arr_to_string(&self.inner.szExeFile)}/// 获取父进程IDpub fn get_parent_pid(&self) -> u32 {self.inner.th32ParentProcessID as u32}/// 获取线程数量pub fn get_thread_count(&self) -> usize {self.inner.cntThreads as usize}/// 获取基础优先权值pub fn get_priority_base(&self) -> i64 {self.inner.pcPriClassBase as i64}/// 获取优先权类别,如果调用进程没有指定权限可能会获取失败,失败时返回 `None`pub fn get_priority_class(&self) -> Option<i32> {let mut priority_class = None;unsafe {let handle = OpenProcess(PROCESS_ALL_ACCESS, 0, self.inner.th32ProcessID);if !handle.is_null() {let class = GetPriorityClass(handle);CloseHandle(handle);priority_class = Some(class as i32);}}priority_class}
}

迭代器实现

迭代器中需要保存一些迭代遍历的状态,因此除了前面保存的快照句柄之外还要存储迭代的索引以及释放句柄的状态,迭代器是不可逆的

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,}}
}

然后就是迭代器的具体实现

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 self.finished {return None;}self.index += 1;let mut entry: PROCESSENTRY32 = unsafe { std::mem::zeroed() };entry.dwSize = size_of::<PROCESSENTRY32>() as u32;//  读取快照中的第一个进程let res = unsafe {if self.index == 1 {Process32First(self.process_snapshot, &mut entry)} else {Process32Next(self.process_snapshot, &mut entry)}};if res == 0 {unsafe {CloseHandle(self.process_snapshot);}self.finished = true;return None;}Some(ProcessInformation::new(entry))}
}

上面的代码有几点需要说明一下:

  1. entry在初始化时需要先到一个全0值的内存空间,并不是分配为一个Rust引用空间,这里用 unsafe 方法 std::mem::zeroed()
  2. 在读取进程Entry之前需要先指定内存长度,这里用 size_of::<PROCESSENTRY32>() 来获取并赋值给 entry.dwSize
  3. 遍历时第一个元素需要调用 Process32First读取,后续的使用 Process32Next 读取
  4. 遍历完时记得关闭快照剧本 使用 CloseHandle 接口

特殊情况处理:如果用户并没有迭代完,上面的代码实现可能会出现快照句柄未释放的情况,所以还需要给迭代器实现一个Drop特征,在释放迭代器时释放快照句柄

impl Drop for ProcessInformationIterator {fn drop(&mut self) {if self.finished {return;}// 释放快照句柄。unsafe {CloseHandle(self.process_snapshot);}self.finished = true;}
}

代码汇总

我在写的时候放在了自定义的utils::process::win包下面,具体引用路径根据自己的情况调整

mod.rs文件

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文件

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};/// [PROCESSENTRY32](https://learn.microsoft.com/zh-cn/windows/win32/api/tlhelp32/ns-tlhelp32-processentry32) 的Rust包装实现
pub struct ProcessInformation {inner: PROCESSENTRY32,
}impl ProcessInformation {pub(crate) fn new(entry: PROCESSENTRY32) -> ProcessInformation {ProcessInformation {inner: entry}}/// 获取进程IDpub fn get_pid(&self) -> u32 {self.inner.th32ProcessID as u32}/// 获取进程名pub fn get_name(&self) -> String {char_arr_to_string(&self.inner.szExeFile)}/// 获取父进程IDpub fn get_parent_pid(&self) -> u32 {self.inner.th32ParentProcessID as u32}/// 获取线程数量pub fn get_thread_count(&self) -> usize {self.inner.cntThreads as usize}/// 获取基础优先权值pub fn get_priority_base(&self) -> i64 {self.inner.pcPriClassBase as i64}/// 获取优先权类别,如果调用进程没有指定权限可能会获取失败,失败时返回 `None`pub fn get_priority_class(&self) -> Option<i32> {let mut priority_class = None;unsafe {let handle = OpenProcess(PROCESS_ALL_ACCESS, 0, self.inner.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 self.finished {return;}// 释放快照句柄。unsafe {CloseHandle(self.process_snapshot);}self.finished = true;}
}impl Iterator for ProcessInformationIterator {type Item = ProcessInformation;fn next(&mut self) -> Option<Self::Item> {if self.finished {return None;}self.index += 1;let mut entry: PROCESSENTRY32 = unsafe { std::mem::zeroed() };entry.dwSize = size_of::<PROCESSENTRY32>() as u32;//  读取快照中的第一个进程let res = unsafe {if self.index == 1 {Process32First(self.process_snapshot, &mut entry)} else {Process32Next(self.process_snapshot, &mut entry)}};if res == 0 {unsafe {CloseHandle(self.process_snapshot);}self.finished = true;return None;}Some(ProcessInformation::new(entry))}
}

测试

测试代码

#[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)}
}

结果

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     smss.exe        4       2       11      None
728     csrss.exe       712     11      13      None
824     wininit.exe     712     1       13      None
832     csrss.exe       816     15      13      None...12624   chrome.exe      12148   19      8       Some(32)
16352   chrome.exe      12148   18      4       Some(64)
14904   chrome.exe      12148   17      4       Some(64)
14672   wslinstaller.exe        892     2       8       None
11160   chrome.exe      12148   20      4       Some(64)
18048   chrome.exe      12148   19      4       Some(64)
5452    chrome.exe      12148   14      4       Some(64)
14468   svchost.exe     892     3       8       None
18060   chrome.exe      12148   14      4       Some(64)
17748   dllhost.exe     688     8       8       Some(32)
16084   vctip.exe       16648   27      8       Some(32)
9008    OpenConsole.exe 10644   6       8       Some(32)
15516   cargo.exe       10644   4       8       Some(32)
11312   cargo.exe       15516   4       8       Some(32)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/60483.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙HarmonyOS 网络请求获取数据Http

注意的是;要为接口返回值指定类型 &#xff0c;以及定义接口数据类型 index.ets import { http } from kit.NetworkKit;interface createAtType {date: number,}interface dataListType {createAt: createAtType;imgUrl: }Component export default struct TabBar {State dat…

2024136读书笔记|《飞鸟集》——使生如夏花之绚烂,死如秋叶之静美

2024136读书笔记|《飞鸟集》——使生如夏花之绚烂&#xff0c;死如秋叶之静美 《飞鸟集》[印]泰戈尔&#xff0c;一本有意思的诗集&#xff0c;中英文对照着读更有意思。“你是谁&#xff0c;读者&#xff0c;百年后读着我的诗&#xff1f;”让我觉得有些久别重逢&#xff0c;忽…

ROS Action

在 ROS 中&#xff0c;Action 是一种支持长时间异步任务的通信机制。与 Service 不同&#xff0c;Action 允许客户端发起一个请求&#xff0c;并在任务执行的过程中不断接收反馈&#xff0c;直到任务完成。这种机制非常适用于可能需要较长时间来完成的任务&#xff0c;比如机器…

C++设计模式和编程框架两种设计元素的比较与相互关系

C设计模式和编程框架都是软件设计中的重要元素&#xff0c;它们各自具有独特的特点和应用场景。以下是对它们的比较&#xff0c;并通过举例说明它们的关系。 C设计模式 定义 设计模式是在软件设计中反复出现的特定问题的解决方案。它们是经过验证的、可复用的设计方案&#…

约束(MYSQL)

not null&#xff08;非空&#xff09; unique&#xff08;唯一&#xff09; default&#xff08;默认约束&#xff0c;规定值&#xff09; 主键约束primary key&#xff08;非空且唯一&#xff09; auto_increment&#xff08;自增类型&#xff09; 复合主键 check&#xff08…

MySQL 权限困境:从权限丢失到权限重生的完整解决方案20241108

&#x1f6e0;️ MySQL 权限困境&#xff1a;从权限丢失到权限重生的完整解决方案 引言 在使用 MySQL 的过程中&#xff0c;我们常常会遇到权限设置不当或丢失的问题&#xff0c;特别是在初次安装或配置更改后。这种权限困境的修复不仅复杂&#xff0c;而且往往是环环相扣&…

笔记 | image may have poor performance,or fail,if run via emulation

在Docker Desktop中现象如图&#xff1a; 当你运行 AMD64 平台代码时&#xff08;Intel 和 AMD 芯&#xff09;&#xff0c;你的 Mac 必须模拟其CPU架构&#xff08;因为你自身是ARM&#xff09;。这通常会非常吃性能。 Docker Desktop 警告你在模拟 Intel/AMD x64 CPU 时性能可…

【C++】C++11特性(上)

✨✨欢迎大家来到Celia的博客✨✨ &#x1f389;&#x1f389;创作不易&#xff0c;请点赞关注&#xff0c;多多支持哦&#x1f389;&#x1f389; 所属专栏&#xff1a;C 个人主页&#xff1a;Celias blog~ 目录 一、列表初始化 二、std::initializer_list 三、右值引用和移…

【WebRTC】视频发送链路中类的简单分析(下)

目录 1.任务队列节流发送器&#xff08;TaskQueuePacedSender&#xff09;1.1 节流控制器添加RTP数据包&#xff08;PacingController::EnqueuePacket()&#xff09;1.2 监测是否要处理Packet&#xff08;PacingController::MaybeProcessPackets()&#xff09; 2.数据包路由&am…

24/11/7 算法笔记 PCA主成分分析

假如我们的数据集是n维的&#xff0c;共有m个数据(x,x,...,x)。我们希望将这m个数据的维度从n维降到k维&#xff0c;希望这m个k维的数据集尽可能的代表原始数据集。我们知道数据从n维降到k维肯定会有损失&#xff0c;但是我们希望损失尽可能的小。那么如何让这k维的数据尽可能表…

JS 实现SSE通讯和了解SSE通讯

SSE 介绍&#xff1a; Server-Sent Events&#xff08;SSE&#xff09;是一种用于实现服务器向客户端实时推送数据的Web技术。与传统的轮询和长轮询相比&#xff0c;SSE提供了更高效和实时的数据推送机制。 SSE基于HTTP协议&#xff0c;允许服务器将数据以事件流&#xff08;…

C/C++每日一练:查找链表的中间节点

链表&#xff08;Linked List&#xff09; 链表是一种线性数据结构&#xff0c;由一系列节点&#xff08;Node&#xff09;通过指针链接在一起。与数组不同&#xff0c;链表中的元素在内存中不需要连续存储&#xff0c;每个节点包含两部分&#xff1a; 数据部分&#xff1a;存…

对称加密与非对称加密:密码学的基石及 RSA 算法详解

对称加密与非对称加密&#xff1a;密码学的基石及 RSA 算法详解 在当今数字化的时代&#xff0c;信息安全至关重要。对称加密和非对称加密作为密码学中的两种基本加密技术&#xff0c;为我们的数据安全提供了强大的保障。本文将深入探讨对称加密和非对称加密的特点、应用场景&…

PH47代码框架全局函数及功能类

PH47代码框架全局函数及功能类 概述 全局函数及功能类体系是PH47框架当中除了4个逻辑层之外最重要的组成部分之一&#xff0c;它们可以在 整个PH7 代码框架及用户代码中使用。常用全局函数及功能类为 PH7 代码框架提供了最常用和最基础的功能实现。 全局函数主要包含了对时间…

Spark 中的 RDD 分区的设定规则与高阶函数、Lambda 表达式详解

Spark 的介绍与搭建&#xff1a;从理论到实践_spark环境搭建-CSDN博客 Spark 的Standalone集群环境安装与测试-CSDN博客 PySpark 本地开发环境搭建与实践-CSDN博客 Spark 程序开发与提交&#xff1a;本地与集群模式全解析-CSDN博客 Spark on YARN&#xff1a;Spark集群模式…

Spring学习笔记_30——事务接口PlatformTransactionManager

PlatformTransactionManager是Spring框架中事务管理的核心接口&#xff0c;它负责管理事务的创建、提交和回滚等操作。 源码 /** Copyright 2002-2020 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you m…

力扣 LeetCode 203. 移除链表元素(Day2:链表)

解题思路&#xff1a; 方法一&#xff1a;头节点和非头节点分开处理 方法二&#xff1a;定义一个dummy虚拟节点&#xff0c;后面的节点就可以采用相同的处理方式 注意&#xff1a; cur需要指向要删除的节点的上一个节点&#xff0c;因为要越过这一个被删除的节点 class Sol…

Python小游戏25——黄金矿工

首先&#xff0c;你需要安装Pygame库。 如果你还没有安装&#xff0c;可以使用以下命令进行安装&#xff1a; 【bash】 pip install pygame 【python】代码展示 import pygame import random # 初始化Pygame pygame.init() # 设置屏幕尺寸 screen_width 800 screen_height 60…

鸿蒙5.0版开发:订阅卡死事件(ArkTS)

在HarmonyOS 5.0中&#xff0c;开发者可以通过ArkTS订阅应用的卡死事件&#xff0c;以便在应用出现卡死时进行相应的处理。这不仅可以帮助开发者监控应用的稳定性&#xff0c;还可以在问题发生时快速定位问题原因。本文将详细介绍如何在ArkTS中订阅卡死事件&#xff0c;并提供示…

IEC60870-5-104 协议源码架构详细分析

IEC60870-5-104 协议源码架构 前言一、资源三、目录层级一二、目录层级二config/lib60870_config.hdependencies/READMEexamplesCMakeLists.txtcs101_master_balancedcs104_client_asyncmulti_client_servertls_clienttls_server说明 make这些文件的作用是否需要导入这些文件&a…