邢冻荔 發表於 2024-11-18 11:35:22

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

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>前言</li><li>依赖</li><li>实现</li><ul class="second_class_ul"><li>创建快照句柄</li><li>实现迭代器</li><li>迭代元素Item</li><li>迭代器实现</li></ul><li>代码汇总</li><ul class="second_class_ul"></ul><li>测试</li><ul class="second_class_ul"><li>测试代码</li></ul></ul></div><p class="maodian"></p><h2>前言</h2>
<p>WIndows API官方文档 提供了C++的调用示例,最近想尝试用Rust去实现,本系列博客记录一下实现过程。</p>
<p class="maodian"></p><h2>依赖</h2>
<p>Rust调用Windows API需要引入依赖<code>winapi</code>,在<code>Cargo.toml</code>中添加依赖</p>
<div class="jb51code"><pre class="brush:plain;">winapi = "0.3.9"</pre></div>
<p>调用不同的API集就需要使用相应的功能<code>features</code>,很好的一个判断方式是你在微软官方文档中看到的是在哪个头文件内,就添加哪个feature,例如本篇文章需要使用 tlhelp32.h 和 processthreadsapi.h 那么就将这俩feature添加进去</p>
<div class="jb51code"><pre class="brush:plain;">winapi = { version = "0.3.9", features = ["tlhelp32", "processthreadsapi"] }</pre></div>
<p class="maodian"></p><h2>实现</h2>
<p><strong>大致步骤</strong>:</p>
<ul><li>创建进程快照,拿到快照句柄</li><li>遍历快照中的进程(以迭代器的方式实现),得到每个进程的数据</li><li>释放快照句柄</li></ul>
<p class="maodian"></p><h3>创建快照句柄</h3>
<p>创建进程快照需要用到 CreateToolhelp32Snapshot 方法,它在 <strong>tlhelp32.h</strong> 头文件中定义。</p>
<div class="jb51code"><pre class="brush:plain;">use winapi::um::tlhelp32::{CreateToolhelp32Snapshot, TH32CS_SNAPPROCESS};
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::errhandlingapi::GetLastError;
/// 保存进程快照并返回进程信息迭代器 `ProcessInformationIterator`
pub fn list() -&gt; Result&lt;ProcessInformationIterator, String&gt; {
    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))
}</pre></div>
<p>代码中的 <strong>ProcessInfomationIterator</strong> 是自定义的,为了结构更清晰,这里使用迭代器模式来读取。</p>
<p>如果保存进程快照失败,返回的句柄会是一个无效的值(这里用了两个条件或的关系去判断是否无效,其实任用其一都可以,他们都表示一个&ldquo;空&rdquo;内存或&ldquo;空&rdquo;指针),使用 <strong>GetLastError</strong> 方法可以获取错误代码,错误代码对应含义见系统错误代码说明,也可以通过API解析成可读文本,这个后面的文章再介绍,这里先用code简单表示一下。</p>
<p class="maodian"></p><h3>实现迭代器</h3>
<p>Rust中的迭代器模式实现方法这里就不多赘述,你只需要知道实现一个迭代器至少需要 <strong>一个迭代元素Item</strong> 和 <strong>一个实现了Iterator特征的迭代器</strong> 就可以了。</p>
<p class="maodian"></p><h3>迭代元素Item</h3>
<div class="jb51code"><pre class="brush:plain;">let vec = vec!
for item in vec {
        ...
}</pre></div>
<p>上面代码的item就是迭代器中具体的元素,因为进程信息有很多,这里就使用一个结构体来存</p>
<div class="jb51code"><pre class="brush:plain;">use winapi::um::tlhelp32::PROCESSENTRY32;
pub struct ProcessInformation {
    inner: PROCESSENTRY32,
}</pre></div>
<p>这里并没有直接将进程的数据解析之后再存入结构体,而是直接将 <strong>PROCESSENTRY32</strong> 结构体做一个包装,这里是为了节省不必要的计算,从句柄中直接读取出来的 PROCESSENTRY32 并不是所有信息都是Rust直接可读的,在需要时才解析,并且通过getter方法读取数据更方便以后拓展,下面是ProcessInformation的具体方法实现。</p>
<div class="jb51code"><pre class="brush:plain;">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: &amp;) -&gt; String {
    chars.into_iter().map(|&amp;c| c as u8 as char).collect()
}
impl ProcessInformation {
    pub(crate) fn new(entry: PROCESSENTRY32) -&gt; ProcessInformation {
      ProcessInformation {
            inner: entry
      }
    }
    /// 获取进程ID
    pub fn get_pid(&amp;self) -&gt; u32 {
      self.inner.th32ProcessID as u32
    }
    /// 获取进程名
    pub fn get_name(&amp;self) -&gt; String {
      char_arr_to_string(&amp;self.inner.szExeFile)
    }
    /// 获取父进程ID
    pub fn get_parent_pid(&amp;self) -&gt; u32 {
      self.inner.th32ParentProcessID as u32
    }
    /// 获取线程数量
    pub fn get_thread_count(&amp;self) -&gt; usize {
      self.inner.cntThreads as usize
    }
    /// 获取基础优先权值
    pub fn get_priority_base(&amp;self) -&gt; i64 {
      self.inner.pcPriClassBase as i64
    }
    /// 获取优先权类别,如果调用进程没有指定权限可能会获取失败,失败时返回 `None`
    pub fn get_priority_class(&amp;self) -&gt; Option&lt;i32&gt; {
      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
    }
}</pre></div>
<p class="maodian"></p><h3>迭代器实现</h3>
<p>迭代器中需要保存一些迭代遍历的状态,因此除了前面保存的快照句柄之外还要存储迭代的索引以及释放句柄的状态,<strong>迭代器是不可逆的</strong>。</p>
<div class="jb51code"><pre class="brush:plain;">use winapi::um::winnt::HANDLE;
pub struct ProcessInformationIterator {
    process_snapshot: HANDLE,
    index: usize,
    finished: bool,
}
impl ProcessInformationIterator {
    pub(crate) fn new(process_snapshot: HANDLE) -&gt; ProcessInformationIterator {
      ProcessInformationIterator {
            process_snapshot,
            index: 0,
            finished: false,
      }
    }
}</pre></div>
<p>然后就是迭代器的具体实现</p>
<div class="jb51code"><pre class="brush:plain;">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(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
      if self.finished {
            return None;
      }
      self.index += 1;
      let mut entry: PROCESSENTRY32 = unsafe { std::mem::zeroed() };
      entry.dwSize = size_of::&lt;PROCESSENTRY32&gt;() as u32;
      //读取快照中的第一个进程
      let res = unsafe {
            if self.index == 1 {
                Process32First(self.process_snapshot, &amp;mut entry)
            } else {
                Process32Next(self.process_snapshot, &amp;mut entry)
            }
      };
      if res == 0 {
            unsafe {
                CloseHandle(self.process_snapshot);
            }
            self.finished = true;
            return None;
      }
      Some(ProcessInformation::new(entry))
    }
}</pre></div>
<p>上面的代码有几点需要说明一下:</p>
<ul><li>entry在初始化时需要先到一个全0值的内存空间,并不是分配为一个Rust引用空间,这里用 unsafe 方法 <code>std::mem::zeroed()</code></li><li>在读取进程Entry之前需要先指定内存长度,这里用 <code>size_of::&lt;PROCESSENTRY32&gt;()</code> 来获取并赋值给 <code>entry.dwSize</code></li><li>遍历时第一个元素需要调用 <code>Process32First</code>读取,后续的使用 <code>Process32Next</code> 读取</li><li>遍历完时记得关闭快照剧本 使用 <code>CloseHandle</code> 接口</li></ul>
<p>特殊情况处理:如果用户并没有迭代完,上面的代码实现可能会出现快照句柄未释放的情况,所以还需要给迭代器实现一个Drop特征,在释放迭代器时释放快照句柄</p>
<div class="jb51code"><pre class="brush:plain;">impl Drop for ProcessInformationIterator {
    fn drop(&amp;mut self) {
      if self.finished {
            return;
      }
      // 释放快照句柄。
      unsafe {
            CloseHandle(self.process_snapshot);
      }
      self.finished = true;
    }
}</pre></div>
<p class="maodian"></p><h2>代码汇总</h2>
<p>我在写的时候放在了自定义的<code>utils::process::win</code>包下面,具体引用路径根据自己的情况调整</p>
<p><code>mod.rs</code>文件</p>
<div class="jb51code"><pre class="brush:plain;">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() -&gt; Result&lt;ProcessInformationIterator, String&gt; {
    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: &amp;) -&gt; String {
    chars.into_iter().map(|&amp;c| c as u8 as char).collect()
}</pre></div>
<p><code>process_information.rs</code>文件</p>
<div class="jb51code"><pre class="brush:plain;">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};
/// (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) -&gt; ProcessInformation {
      ProcessInformation {
            inner: entry
      }
    }
    /// 获取进程ID
    pub fn get_pid(&amp;self) -&gt; u32 {
      self.inner.th32ProcessID as u32
    }
    /// 获取进程名
    pub fn get_name(&amp;self) -&gt; String {
      char_arr_to_string(&amp;self.inner.szExeFile)
    }
    /// 获取父进程ID
    pub fn get_parent_pid(&amp;self) -&gt; u32 {
      self.inner.th32ParentProcessID as u32
    }
    /// 获取线程数量
    pub fn get_thread_count(&amp;self) -&gt; usize {
      self.inner.cntThreads as usize
    }
    /// 获取基础优先权值
    pub fn get_priority_base(&amp;self) -&gt; i64 {
      self.inner.pcPriClassBase as i64
    }
    /// 获取优先权类别,如果调用进程没有指定权限可能会获取失败,失败时返回 `None`
    pub fn get_priority_class(&amp;self) -&gt; Option&lt;i32&gt; {
      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) -&gt; ProcessInformationIterator {
      ProcessInformationIterator {
            process_snapshot,
            index: 0,
            finished: false,
      }
    }
}
impl Drop for ProcessInformationIterator {
    fn drop(&amp;mut self) {
      if self.finished {
            return;
      }
      // 释放快照句柄。
      unsafe {
            CloseHandle(self.process_snapshot);
      }
      self.finished = true;
    }
}
impl Iterator for ProcessInformationIterator {
    type Item = ProcessInformation;
    fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
      if self.finished {
            return None;
      }
      self.index += 1;
      let mut entry: PROCESSENTRY32 = unsafe { std::mem::zeroed() };
      entry.dwSize = size_of::&lt;PROCESSENTRY32&gt;() as u32;
      //读取快照中的第一个进程
      let res = unsafe {
            if self.index == 1 {
                Process32First(self.process_snapshot, &amp;mut entry)
            } else {
                Process32Next(self.process_snapshot, &amp;mut entry)
            }
      };
      if res == 0 {
            unsafe {
                CloseHandle(self.process_snapshot);
            }
            self.finished = true;
            return None;
      }
      Some(ProcessInformation::new(entry))
    }
}</pre></div>
<p class="maodian"></p><h2>测试</h2>
<p class="maodian"></p><h3>测试代码</h3>
<div class="jb51code"><pre class="brush:plain;">#
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)
    }
}</pre></div>
<p>结果</p>
<div class="jb51code"><pre class="brush:plain;">PID   Name    Parent PID      Threads Priority Base   Priority Class
0             0       6       0       None
4       System0       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)</pre></div>
<p>到此这篇关于Rust调用Windows API 获取正在运行的全部进程信息的文章就介绍到这了,更多相关Rust调用Windows API 内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>一步到位,教你如何在Windows成功安装Rust</li><li>RustDesk Server服务器搭建教程含api服务器和webclient服务器</li><li>Rust如何使用config配置API</li><li>Rust&nbsp;for循环语法糖背后的API场景分析</li><li>Rust捕获全局panic并记录进程退出日志的方法</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: Rust调用Windows API 如何获取正在运行的全部进程信息