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

    /// 获取进程ID
    pub fn get_pid(&self) -> u32 {
        self.inner.th32ProcessID as u32
    }

    /// 获取进程名
    pub fn get_name(&self) -> String {
        char_arr_to_string(&self.inner.szExeFile)
    }

    /// 获取父进程ID
    pub 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
        }
    }

    /// 获取进程ID
    pub fn get_pid(&self) -> u32 {
        self.inner.th32ProcessID as u32
    }

    /// 获取进程名
    pub fn get_name(&self) -> String {
        char_arr_to_string(&self.inner.szExeFile)
    }

    /// 获取父进程ID
    pub 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)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MyBatisPlus(Spring Boot版)的基本使用

1. 初始化项目 1.1. 配置application.yml spring:# 配置数据源信息datasource:# 配置数据源类型type: com.zaxxer.hikari.HikariDataSource# 配置连接数据库信息driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatis_plus?characterEncodi…

【MongoDB】MongoDB的集群,部署架构,OptLog,集群优化等详解

文章目录 一、引入复制集的原因二、复制集成员&#xff08;一&#xff09;基本成员&#xff08;二&#xff09;主节点&#xff08;Primary&#xff09;细化成员 三、复制集常见部署架构&#xff08;一&#xff09;基础三节点&#xff08;二&#xff09;跨数据中心 四、复制集保…

Golang | Leetcode Golang题解之第564题寻找最近的回文数

题目&#xff1a; 题解&#xff1a; func nearestPalindromic(n string) string {m : len(n)candidates : []int{int(math.Pow10(m-1)) - 1, int(math.Pow10(m)) 1}selfPrefix, _ : strconv.Atoi(n[:(m1)/2])for _, x : range []int{selfPrefix - 1, selfPrefix, selfPrefix …

git根据远程分支创建本地新分支

比如我当前本地仓库有4个 remote 仓库&#xff0c;我希望根据其中的一个 <remote>/<branch> 创建本地分支&#xff1a; 先使用 github fetch <remote> 拉取 <remote> 的分支信息&#xff0c;然后在 git checkout -b 创建新分支时使用 -t <remote>…

r-and-r——提高长文本质量保证任务的准确性重新提示和上下文搜索的新方法可减轻大规模语言模型中的迷失在中间现象

概述 随着大规模语言模型的兴起&#xff0c;自然语言处理领域取得了重大发展。这些创新的模型允许用户通过输入简单的 "提示 "文本来执行各种任务。然而&#xff0c;众所周知&#xff0c;在问题解答&#xff08;QA&#xff09;任务中&#xff0c;用户在处理长文本时…

Redis 概 述 和 安 装

安 装 r e d i s: 1. 下 载 r e dis h t t p s : / / d o w n l o a d . r e d i s . i o / r e l e a s e s / 2. 将 redis 安装包拷贝到 /opt/ 目录 3. 解压 tar -zvxf redis-6.2.1.tar.gz 4. 安装gcc yum install gcc 5. 进入目录 cd redis-6.2.1 6. 编译 make …

Android 项目依赖库无法找到的解决方案

目录 错误信息解析 解决方案 1. 检查依赖版本 2. 检查 Maven 仓库配置 3. 强制刷新 Gradle 缓存 4. 检查网络连接 5. 手动下载依赖 总结 相关推荐 最近&#xff0c;我在编译一个 Android 老项目时遇到了一个问题&#xff0c;错误信息显示无法找到 com.gyf.immersionba…

北航软件算法C4--图部分

C4上级图部分 TOPO!步骤代码段TOPO排序部分 完整代码 简单的图图题目描述输入输出样例步骤代码段开辟vector容器作为dist二维数组初始化调用Floyd算法查询 完整代码 负环题目描述输入输出样例步骤代码段全局变量定义spfa1函数用于判断是否有负环spfa2用于记录每个点到1号点的距…

ks 小程序sig3

前言 搞了app版的快手之后 &#xff08;被风控麻了&#xff09; 于是试下vx小程序版的 抓包调试 小程序抓包问题 网上很多教程&#xff0c; github也有开源的工具代码 自行搜索 因为我们需要调试代码&#xff0c;所以就用了下开源的工具 &#xff08;可以用chrome的F12功能&a…

docker:docker: Get https://registry-1.docker.io/v2/: net/http: request canceled

无数次的拉镜像让人崩溃&#xff1a; rootnode11:~/ragflow/docker# more rag.sh #export HTTP_PROXYhttp://192.168.207.127:7890 #export HTTPS_PROXYhttp://192.168.207.127:7890 #export NO_PROXYlocalhost,127.0.0.1,.aliyun.com docker compose -f docker-compose-gpu-C…

基于java Springboot高校失物招领平台

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

菜鸟驿站二维码/一维码 取件识别功能

特别注意需要引入 库文 ZXing 可跳转&#xff1a; 记录【WinForm】C#学习使用ZXing.Net生成条码过程_c# zxing-CSDN博客 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using static System.Net.…

使用WebRTC实现点对点实时音视频通信的技术详解

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用WebRTC实现点对点实时音视频通信的技术详解 使用WebRTC实现点对点实时音视频通信的技术详解 使用WebRTC实现点对点实时音视频…

执行flink sql连接clickhouse库

手把手教学&#xff0c;flink connector打通clickhouse大数据库&#xff0c;通过下发flink sql&#xff0c;来使用ck。 组件版本jdk1.8flink1.17.2clickhouse23.12.2.59 1.背景 flink官方不支持clickhouse连接器&#xff0c;工作中难免会用到。 2.方案 利用GitHub大佬提供…

力扣(leetcode)题目总结——辅助栈篇

leetcode 经典题分类 链表数组字符串哈希表二分法双指针滑动窗口递归/回溯动态规划二叉树辅助栈 本系列专栏&#xff1a;点击进入 leetcode题目分类 关注走一波 前言&#xff1a;本系列文章初衷是为了按类别整理出力扣&#xff08;leetcode&#xff09;最经典题目&#xff0c…

基于Java Springboot宠物猫售卖管理系统

一、作品包含 源码数据库全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据库&#xff1a;…

Windows docker下载minio出现“Using default tag: latestError response from daemon”

Windows docker下载minio出现 Using default tag: latest Error response from daemon: Get "https://registry-1.docker.io/v2/": context deadline exceeded 此类情况&#xff0c;一般为镜像地址问题。 {"registry-mirrors": ["https://docker.re…

数据结构查找-哈希表(开发地址法+线性探测法)+(创建+查找+删除代码)+(C语言代码)

#include<stdlib.h> #include<stdio.h> #include<stdbool.h> #define NULLKEY -1//单元为空 #define DELKEY -2//单元内容被删除 #define M 20 typedef struct {int key;//关键字int count;//统计哈希冲突探测次数 }HashTable; //插入到哈希表 void InsertHT…

视频直播5G CPE解决方案:ZX7981PG/ZX7981PMWIFI6网络覆盖

方案背景 视频直播蓬勃发展的当下&#xff0c;传统直播网络联网方式的局限性越来越明显。目前传统直播的局限性主要集中在以下几个方面&#xff1a; 传统直播间网络架构条件有限&#xff0c;可连接WIFI数量少&#xff0c;多终端同时直播难以维持&#xff1b;目前4G网络带宽有限…

【电子设计】按键LED控制与FreeRTOS

1. 安装Keilv5 打开野火资料,寻找软件包 解压后得到的信息 百度网盘 请输入提取码 提取码:gfpp 安装526或者533版本都可以 下载需要的 F1、F4、F7、H7 名字的 DFP pack 芯片包 安装完 keil 后直接双击安装 注册操作,解压注册文件夹后根据里面的图示步骤操作 打开说明 STM…