1#[cfg(feature = "sysinfo")]
5use std::path::PathBuf;
6#[cfg(feature = "std")]
7use std::{
8 mem::{MaybeUninit, transmute},
9 time::SystemTime,
10};
11
12use derive_more::{Deref, Display};
13use windows::Win32::{
14 Foundation::{GetLastError, HWND, WIN32_ERROR},
15 System::Threading::{
16 GetProcessIdOfThread, GetProcessTimes, OpenProcess, OpenThread,
17 PROCESS_QUERY_LIMITED_INFORMATION, THREAD_QUERY_LIMITED_INFORMATION,
18 },
19 UI::WindowsAndMessaging::GetWindowThreadProcessId,
20};
21
22use crate::log::*;
23
24#[cfg(feature = "std")]
25mod gui;
26pub mod module;
27
28#[cfg(feature = "std")]
29pub use gui::*;
30
31#[derive(Clone, Copy, PartialEq, Eq, Hash, Display, Debug, Deref)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34pub struct Pid(pub u32);
35
36impl Pid {
37 #[cfg(feature = "std")]
38 pub fn current() -> Self {
39 Self(std::process::id())
40 }
41
42 pub fn from_tid(tid: u32) -> windows::core::Result<Self> {
43 let thread = unsafe { OpenThread(THREAD_QUERY_LIMITED_INFORMATION, false, tid) }?;
44 match unsafe { GetProcessIdOfThread(thread) } {
45 0 => Err(windows::core::Error::from_thread()),
46 pid => Ok(Pid(pid)),
47 }
48 }
49
50 fn from_hwnd_with_thread(hwnd: HWND) -> Result<(Self, u32), WIN32_ERROR> {
51 let mut pid: u32 = 0;
52 let tid = unsafe { GetWindowThreadProcessId(hwnd, Some(&mut pid)) };
53 if tid != 0 {
54 Ok((Pid(pid), tid))
55 } else {
56 Err(unsafe { GetLastError() })
57 }
58 }
59
60 pub fn from_hwnd(hwnd: HWND) -> Result<Self, WIN32_ERROR> {
64 Self::try_from(hwnd)
65 }
66}
67
68impl TryFrom<HWND> for Pid {
69 type Error = WIN32_ERROR;
70
71 fn try_from(hwnd: HWND) -> Result<Self, Self::Error> {
75 Self::from_hwnd_with_thread(hwnd).map(|(pid, _tid)| pid)
76 }
77}
78
79#[cfg(feature = "std")]
80impl Pid {
81 pub fn get_start_time(self) -> windows::core::Result<SystemTime> {
83 let pid = self.0;
84 let handle = unsafe { OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid) }?;
85
86 let mut start: MaybeUninit<SystemTime> = MaybeUninit::uninit();
88 let mut x = Default::default();
89 unsafe { GetProcessTimes(handle, &mut start as *mut _ as _, &mut x, &mut x, &mut x) }?;
90
91 Ok(unsafe { start.assume_init() })
92 }
93
94 pub fn get_start_time_or_max(self) -> SystemTime {
98 self.get_start_time()
99 .inspect_err(|e| debug!(%e, "get_start_time"))
100 .unwrap_or_else(|_| {
101 unsafe { transmute(i64::MAX) }
104 })
105 }
106}
107
108#[cfg(feature = "sysinfo")]
109impl Pid {
110 pub fn with_process<R>(
111 self,
112 refresh_info: sysinfo::ProcessRefreshKind,
113 f: impl FnOnce(&sysinfo::Process) -> R,
114 ) -> Option<R> {
115 let pid = self.clone().into();
116 let mut system = sysinfo::System::new();
117 system.refresh_processes_specifics(
118 sysinfo::ProcessesToUpdate::Some(&[pid]),
119 false,
120 refresh_info,
121 );
122 system.process(pid).map(f)
123 }
124
125 pub fn image_path(self) -> Option<PathBuf> {
126 self.with_process(
127 sysinfo::ProcessRefreshKind::nothing().with_exe(sysinfo::UpdateKind::Always),
128 |p| p.exe().map(|p| p.to_owned()),
129 )
130 .flatten()
131 }
132}
133
134#[cfg(feature = "sysinfo")]
135impl From<sysinfo::Pid> for Pid {
136 fn from(pid: sysinfo::Pid) -> Self {
137 Self(pid.as_u32())
138 }
139}
140
141#[cfg(feature = "sysinfo")]
142impl Into<sysinfo::Pid> for Pid {
143 fn into(self) -> sysinfo::Pid {
144 sysinfo::Pid::from_u32(self.0)
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 use windows::Win32::UI::WindowsAndMessaging::GetDesktopWindow;
153
154 #[test]
155 fn pid_from_hwnd() {
156 let desktop = unsafe { GetDesktopWindow() };
158 let pid = Pid::from_hwnd(desktop);
159
160 assert!(pid.is_ok(), "Should be able to get PID from desktop window");
162 println!("Desktop window PID: {:?}", pid.unwrap());
163 }
164
165 #[test]
166 #[cfg(feature = "sysinfo")]
167 fn pid_from_hwnd_desktop() {
168 let desktop = unsafe { GetDesktopWindow() };
169 dbg!(desktop);
170 let pid = Pid::from_hwnd(desktop).expect("Should get PID from desktop window");
171 dbg!(pid);
172
173 let exe_path = pid.image_path();
174 assert_eq!(exe_path, None);
176 }
177
178 #[test]
180 #[cfg(feature = "sysinfo")]
181 fn pid_from_hwnd_shell() {
182 use std::path::Path;
183 use windows::Win32::UI::WindowsAndMessaging::GetShellWindow;
184
185 let desktop = unsafe { GetShellWindow() };
186 dbg!(desktop);
187 let pid = Pid::from_hwnd(desktop).expect("Should get PID from desktop window");
188 dbg!(pid);
189
190 let exe_path = pid.image_path().unwrap();
192 assert_eq!(exe_path, Path::new(r"C:\Windows\explorer.exe"));
193 }
194
195 #[test]
196 fn get_start_time() {
197 let current_pid = std::process::id();
198 let pid = Pid(current_pid);
199
200 let start_time = pid.get_start_time().unwrap();
201 dbg!(start_time);
202 let t = start_time
203 .duration_since(SystemTime::UNIX_EPOCH)
204 .unwrap()
205 .as_secs();
206 assert!(t > 0, "{t}");
207
208 let start_time2 = pid.get_start_time().unwrap();
209 dbg!(start_time2);
210 assert_eq!(start_time, start_time2);
211 }
212}