win32_ecoqos/utils/
thread.rs1use std::{
2 ffi::{OsStr, OsString},
3 os::windows::ffi::OsStringExt,
4};
5
6use windows::Win32::{
7 Foundation::{CloseHandle, HANDLE},
8 System::{
9 Diagnostics::ToolHelp::{
10 CreateToolhelp32Snapshot, Thread32First, Thread32Next, TH32CS_SNAPTHREAD, THREADENTRY32,
11 },
12 Threading::{GetThreadDescription, OpenThread, THREAD_QUERY_LIMITED_INFORMATION},
13 },
14};
15
16#[derive(Debug, PartialEq, Eq)]
17pub struct Thread {
19 pub thread_id: u32,
21 pub owner_process_id: u32,
23}
24
25#[derive(Debug)]
46pub struct Threads {
47 snapshot: HANDLE,
48 last_entry: Option<THREADENTRY32>,
49}
50
51impl Drop for Threads {
52 fn drop(&mut self) {
53 let _ = unsafe { CloseHandle(self.snapshot) };
54 }
55}
56
57impl Thread {
58 pub fn get_name(&self) -> windows_result::Result<OsString> {
59 unsafe {
60 let hthread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, false, self.thread_id)?;
61 let description = GetThreadDescription(hthread)?;
62
63 Ok(OsString::from_wide(description.as_wide()))
64 }
65 }
66}
67
68impl Threads {
69 pub fn try_new() -> windows_result::Result<Self> {
71 let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, std::process::id()) }?;
72 Ok(Self {
73 snapshot,
74 last_entry: None,
75 })
76 }
77
78 pub fn find_thread_by_name<'a>(
80 self,
81 thread_name: &'a OsStr,
82 full_match: bool,
83 ) -> impl Iterator<Item = Thread> + 'a {
84 let process_id = std::process::id();
85 self.filter(move |t| t.owner_process_id == process_id)
86 .filter(move |t| {
87 t.get_name().is_ok_and(|name| {
88 if full_match {
89 &name == thread_name
90 } else {
91 name.to_string_lossy()
92 .contains(thread_name.to_string_lossy().as_ref())
93 }
94 })
95 })
96 }
97}
98
99impl Iterator for Threads {
100 type Item = Thread;
101
102 fn next(&mut self) -> Option<Self::Item> {
103 let first = self.last_entry.is_none();
104 let mut entry = self.last_entry.take().unwrap_or(THREADENTRY32 {
105 dwSize: size_of::<THREADENTRY32>() as u32,
106 ..Default::default()
107 });
108
109 unsafe {
110 if first {
111 Thread32First(self.snapshot, &mut entry as *mut _)
112 } else {
113 Thread32Next(self.snapshot, &mut entry as *mut _)
114 }
115 }
116 .ok()
117 .map(|_| entry)
118 .inspect(|entry| self.last_entry = Some(*entry))
119 .map(
120 |THREADENTRY32 {
121 th32ThreadID,
122 th32OwnerProcessID,
123 ..
124 }| {
125 Thread {
126 thread_id: th32ThreadID,
127 owner_process_id: th32OwnerProcessID,
128 }
129 },
130 )
131 }
132}