Skip to main content

windows_erg/process/
list.rs

1//! Process listing and enumeration.
2
3use windows::Win32::Foundation::{CloseHandle, ERROR_NO_MORE_FILES};
4use windows::Win32::System::Diagnostics::ToolHelp::{
5    CreateToolhelp32Snapshot, PROCESSENTRY32W, Process32FirstW, Process32NextW, TH32CS_SNAPPROCESS,
6};
7
8use super::processes::Process;
9use super::types::{ProcessId, ProcessInfo};
10use crate::error::{Error, ProcessError, ProcessOpenError, Result};
11
12impl Process {
13    /// List all processes in the system.
14    pub fn list() -> Result<Vec<ProcessInfo>> {
15        let mut buffer = Vec::with_capacity(128);
16        Self::list_with_buffer(&mut buffer)?;
17        Ok(buffer)
18    }
19
20    /// List all processes using a reusable output buffer.
21    ///
22    /// Returns the number of processes found and added to the buffer.
23    pub fn list_with_buffer(out_processes: &mut Vec<ProcessInfo>) -> Result<usize> {
24        Self::list_with_filter_impl(out_processes, |_| true)
25    }
26
27    /// List all processes matching a filter using a reusable output buffer.
28    ///
29    /// The filter function is called for each process. Only processes where the filter
30    /// returns `true` are added to the buffer. This is more efficient than listing all
31    /// and filtering afterwards as it avoids unnecessary buffer operations.
32    ///
33    /// Returns the number of matching processes found and added to the buffer.
34    pub fn list_with_filter<F>(out_processes: &mut Vec<ProcessInfo>, filter: F) -> Result<usize>
35    where
36        F: Fn(&ProcessInfo) -> bool,
37    {
38        Self::list_with_filter_impl(out_processes, filter)
39    }
40
41    /// Internal: Common implementation for list enumeration with optional filtering.
42    fn list_with_filter_impl<F>(out_processes: &mut Vec<ProcessInfo>, filter: F) -> Result<usize>
43    where
44        F: Fn(&ProcessInfo) -> bool,
45    {
46        // Clear the output buffer for reuse
47        out_processes.clear();
48
49        let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }.map_err(|e| {
50            Error::Process(ProcessError::OpenFailed(ProcessOpenError::with_code(
51                0,
52                "Failed to create process snapshot",
53                e.code().0,
54            )))
55        })?;
56
57        let mut entry = PROCESSENTRY32W {
58            dwSize: std::mem::size_of::<PROCESSENTRY32W>() as u32,
59            ..Default::default()
60        };
61
62        if unsafe { Process32FirstW(snapshot, &mut entry) }.is_ok() {
63            loop {
64                // Extract process name
65                let name_end = entry
66                    .szExeFile
67                    .iter()
68                    .position(|&c| c == 0)
69                    .unwrap_or(entry.szExeFile.len());
70                let name = String::from_utf16_lossy(&entry.szExeFile[..name_end]);
71
72                let parent_pid = if entry.th32ParentProcessID == 0 {
73                    None
74                } else {
75                    Some(ProcessId::new(entry.th32ParentProcessID))
76                };
77
78                let process_info = ProcessInfo {
79                    pid: ProcessId::new(entry.th32ProcessID),
80                    parent_pid,
81                    name,
82                    thread_count: entry.cntThreads,
83                };
84
85                // Apply filter and add to output buffer if it matches
86                if filter(&process_info) {
87                    out_processes.push(process_info);
88                }
89
90                // Get next entry
91                match unsafe { Process32NextW(snapshot, &mut entry) } {
92                    Ok(_) => continue,
93                    Err(e) if e.code() == ERROR_NO_MORE_FILES.into() => break,
94                    Err(_) => break,
95                }
96            }
97        }
98
99        unsafe {
100            let _ = CloseHandle(snapshot);
101        }
102        Ok(out_processes.len())
103    }
104
105    /// Get the parent process ID.
106    pub fn parent_id(&self) -> Result<Option<ProcessId>> {
107        // We need to enumerate processes to find parent
108        let processes = Self::list()?;
109
110        for proc in processes {
111            if proc.pid == self.id() {
112                return Ok(proc.parent_pid);
113            }
114        }
115
116        Ok(None)
117    }
118
119    /// Get all immediate child processes.
120    pub fn children(&self) -> Result<Vec<ProcessId>> {
121        let mut buffer = Vec::with_capacity(128);
122        self.children_with_buffer(&mut buffer)?;
123        // Convert buffer of ProcessInfo to Vec<ProcessId>
124        Ok(buffer.into_iter().map(|p| p.pid).collect())
125    }
126
127    /// Get all immediate child processes using a reusable buffer.
128    pub fn children_with_buffer(&self, buffer: &mut Vec<ProcessInfo>) -> Result<usize> {
129        let self_id = self.id();
130        Self::list_with_filter(buffer, |p| p.parent_pid == Some(self_id))
131    }
132}