Skip to main content

windows_erg/process/
threads.rs

1//! Thread enumeration and operations.
2
3use windows::Win32::Foundation::{CloseHandle, ERROR_NO_MORE_FILES};
4use windows::Win32::System::Diagnostics::ToolHelp::{
5    CreateToolhelp32Snapshot, TH32CS_SNAPTHREAD, THREADENTRY32, Thread32First, Thread32Next,
6};
7
8use super::processes::Process;
9use super::types::{ProcessId, ThreadId, ThreadInfo};
10use crate::error::{Error, ProcessError, ProcessOpenError, Result};
11
12impl Process {
13    /// Enumerate all threads in this process.
14    pub fn threads(&self) -> Result<Vec<ThreadInfo>> {
15        let mut buffer = Vec::with_capacity(256);
16        self.threads_with_buffer(&mut buffer)?;
17        Ok(buffer)
18    }
19
20    /// Enumerate threads using a reusable output buffer.
21    pub fn threads_with_buffer(&self, out_threads: &mut Vec<ThreadInfo>) -> Result<usize> {
22        self.threads_with_filter(out_threads, |_| true)
23    }
24
25    /// Enumerate threads matching a filter using a reusable output buffer.
26    ///
27    /// The filter function is called for each thread. Only threads where the filter
28    /// returns `true` are added to the output buffer. This is more efficient than enumerating all
29    /// and filtering afterwards.
30    ///
31    /// Returns the number of matching threads found and added to the buffer.
32    pub fn threads_with_filter<F>(
33        &self,
34        out_threads: &mut Vec<ThreadInfo>,
35        filter: F,
36    ) -> Result<usize>
37    where
38        F: Fn(&ThreadInfo) -> bool,
39    {
40        out_threads.clear();
41
42        let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0) }.map_err(|e| {
43            Error::Process(ProcessError::OpenFailed(ProcessOpenError::with_code(
44                self.id().as_u32(),
45                "Failed to create thread snapshot",
46                e.code().0,
47            )))
48        })?;
49
50        let mut entry = THREADENTRY32 {
51            dwSize: std::mem::size_of::<THREADENTRY32>() as u32,
52            ..Default::default()
53        };
54        let process_id = self.id().as_u32();
55
56        if unsafe { Thread32First(snapshot, &mut entry) }.is_ok() {
57            loop {
58                // Only include threads from this process
59                if entry.th32OwnerProcessID == process_id {
60                    let thread_info = ThreadInfo {
61                        tid: ThreadId::new(entry.th32ThreadID),
62                        pid: ProcessId::new(entry.th32OwnerProcessID),
63                        base_priority: entry.tpBasePri,
64                    };
65
66                    // Apply filter and add to output buffer if it matches
67                    if filter(&thread_info) {
68                        out_threads.push(thread_info);
69                    }
70                }
71
72                // Get next entry
73                match unsafe { Thread32Next(snapshot, &mut entry) } {
74                    Ok(_) => continue,
75                    Err(e) if e.code() == ERROR_NO_MORE_FILES.into() => break,
76                    Err(_) => break,
77                }
78            }
79        }
80
81        unsafe {
82            let _ = CloseHandle(snapshot);
83        }
84        Ok(out_threads.len())
85    }
86}