1use std::{
2 ffi::OsString,
3 os::windows::ffi::OsStrExt,
4 path::{Path, PathBuf},
5 str::FromStr,
6};
7
8use crate::{get_thread_proc_id, string_conv::ToUtf8String, ProcessInfo};
9use anyhow::{anyhow, Result as AnyResult};
10use windows::{
11 core::HSTRING,
12 Wdk::System::Threading::{NtQueryInformationProcess, ProcessBasicInformation},
13 Win32::{
14 Foundation::{CloseHandle, HANDLE, HWND, MAX_PATH, UNICODE_STRING},
15 Globalization::GetSystemDefaultLangID,
16 Graphics::Gdi::{
17 MonitorFromWindow, HMONITOR, MONITOR_DEFAULTTONEAREST, MONITOR_DEFAULTTONULL,
18 },
19 Storage::FileSystem::{
20 GetFileVersionInfoExW, GetFileVersionInfoSizeExW, VerQueryValueW, FILE_VER_GET_NEUTRAL,
21 },
22 System::{
23 Diagnostics::Debug::ReadProcessMemory,
24 ProcessStatus::GetModuleFileNameExW,
25 Threading::{
26 OpenProcess, PROCESS_BASIC_INFORMATION, PROCESS_QUERY_INFORMATION,
27 PROCESS_TERMINATE, PROCESS_VM_READ,
28 },
29 },
30 UI::WindowsAndMessaging::{
31 GetClassNameW, GetWindowTextLengthW, GetWindowTextW,
32 },
33 },
34};
35use windows_result::Error;
36
37const SZ_STRING_FILE_INFO: &'static str = "StringFileInfo";
38const SZ_PRODUCT_NAME: &'static str = "ProductName";
39const SZ_HEX_CODE_PAGE_ID_UNICODE: &'static str = "04B0";
40
41pub fn get_exe(handle: HWND) -> AnyResult<(u32, PathBuf)> {
55 let ProcessInfo { process_id: proc_id, .. } = get_thread_proc_id(handle)?;
56 let h_proc = unsafe {
57 OpenProcess(
58 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE,
59 false,
60 proc_id,
61 )?
62 };
63
64 let exe = unsafe {
65 let mut path = [0 as u16; MAX_PATH as usize];
66 let res = GetModuleFileNameExW(Some(h_proc), None, &mut path);
68 if res > 0 {
69 Ok::<String, anyhow::Error>(path.as_ref().to_utf8())
70 } else {
71 Err(Error::from_win32().into())
72 }
73 }?;
74
75 unsafe {
76 CloseHandle(h_proc)?;
77 }
78
79 Ok((proc_id, PathBuf::from_str(&exe)?))
80}
81
82pub fn get_title(handle: HWND) -> AnyResult<String> {
83 let len = unsafe { GetWindowTextLengthW(handle) };
84 if len == 0 {
85 return Err(Error::from_win32().into());
86 }
87
88 let len = TryInto::<usize>::try_into(len)?;
89
90 let mut title = vec![0 as u16; len + 1];
91 let get_title_res = unsafe { GetWindowTextW(handle, &mut title) };
92 if get_title_res == 0 {
93 return Err(Error::from_win32().into());
94 }
95
96 Ok(title.to_utf8())
97}
98
99pub fn get_window_class(handle: HWND) -> AnyResult<String> {
100 let mut class = [0 as u16; MAX_PATH as usize +1];
101
102 let len = unsafe { GetClassNameW(handle, &mut class) };
103 if len == 0 {
104 return Err(Error::from_win32().into());
105 }
106
107 Ok(class.as_ref().to_utf8())
108}
109
110pub fn get_product_name(full_exe: &Path) -> AnyResult<String> {
111 let exe_wide = HSTRING::from(full_exe.as_os_str());
112
113 let mut dummy = 0;
114 let required_buffer_size =
115 unsafe { GetFileVersionInfoSizeExW(FILE_VER_GET_NEUTRAL, &exe_wide, &mut dummy) };
116 if required_buffer_size == 0 {
117 return Err(Error::from_win32().into());
118 }
119
120 let mut buffer: Vec<u16> = vec![0; required_buffer_size as usize];
121 unsafe {
122 GetFileVersionInfoExW(
123 FILE_VER_GET_NEUTRAL,
124 &exe_wide,
125 None,
126 required_buffer_size,
127 buffer.as_mut_ptr() as *mut _,
128 )?;
129 }
130
131 let lang_id = unsafe { GetSystemDefaultLangID() };
132 let query_key: Vec<u16> = OsString::from(format!(
133 "\\{}\\{}{}\\{}",
134 SZ_STRING_FILE_INFO, lang_id, SZ_HEX_CODE_PAGE_ID_UNICODE, SZ_PRODUCT_NAME
135 ))
136 .encode_wide()
137 .collect();
138 let query_key = HSTRING::from_wide(&query_key);
139
140 let mut pages_ptr: *mut u16 = std::ptr::null_mut();
141 let mut pages_length = 0;
142
143 unsafe {
144 VerQueryValueW(
145 buffer.as_mut_ptr() as _,
146 &query_key,
147 &mut pages_ptr as *mut _ as _,
148 &mut pages_length,
149 )
150 .ok()?
151 };
152
153 let chars_in_buf = required_buffer_size / (std::mem::size_of::<u16>() as u32);
154 if pages_ptr.is_null() || chars_in_buf < pages_length {
155 return Err(anyhow!("Invalid state"));
156 }
157
158 let product_name = unsafe { std::slice::from_raw_parts(pages_ptr, pages_length as usize - 1) };
159 let product_name = String::from_utf16_lossy(product_name);
160
161 Ok(product_name)
162}
163
164pub fn hwnd_to_monitor(handle: HWND) -> AnyResult<HMONITOR> {
165 unsafe {
166 let res = MonitorFromWindow(handle, MONITOR_DEFAULTTONEAREST);
167 if res.is_invalid() {
168 return Err(Error::from_win32().into());
169 }
170
171 Ok(res)
172 }
173}
174
175pub fn intersects_with_multiple_monitors(handle: HWND) -> AnyResult<bool> {
176 unsafe {
177 let res = MonitorFromWindow(handle, MONITOR_DEFAULTTONULL);
178
179 return Ok(!res.is_invalid());
180 }
181}
182
183pub fn get_command_line_args(wnd: HWND) -> AnyResult<String> {
184 let ProcessInfo { process_id: proc_id, ..} = get_thread_proc_id(wnd)?;
185
186 let handle = unsafe {
187 OpenProcess(
188 PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, false, proc_id, )?
192 };
193
194 if handle.is_invalid() {
195 return Err(Error::from_win32().into());
196 }
197
198 let res = unsafe { get_command_line_args_priv(handle) };
199 unsafe {
200 CloseHandle(handle)?;
201 }
202
203 res
204}
205
206unsafe fn get_command_line_args_priv(handle: HANDLE) -> AnyResult<String> {
207 let mut pbi = PROCESS_BASIC_INFORMATION::default();
208 NtQueryInformationProcess(
210 handle,
211 ProcessBasicInformation,
212 &mut pbi as *mut _ as _,
213 size_of_val(&pbi) as u32,
214 std::ptr::null_mut(),
215 )
216 .ok()?;
217
218 let process_parameter_offset = 0x20;
220 let command_line_offset = 0x70;
221
222 let peb_size = process_parameter_offset + 8;
224 let mut peb = vec![0u8; peb_size];
225
226 let pp_size = command_line_offset + 16;
228 let mut pp = vec![0u8; pp_size];
229
230 ReadProcessMemory(
232 handle,
233 pbi.PebBaseAddress as _,
234 peb.as_mut_ptr() as _,
235 peb_size,
236 None,
237 )?;
238
239 let parameters = *(peb.as_ptr().add(process_parameter_offset) as *const *const u8); ReadProcessMemory(
243 handle, parameters as _, pp.as_mut_ptr() as _, pp_size, None, )?;
249
250 let ptr_cmd_line = pp
251 .as_ptr() .add(command_line_offset) as *const UNICODE_STRING;
253
254 let maximum_len = (*ptr_cmd_line).MaximumLength as usize;
255 let mut cmd_line = vec![0u16; maximum_len];
256
257 ReadProcessMemory(
258 handle,
259 (*ptr_cmd_line).Buffer.as_ptr() as _,
260 cmd_line.as_mut_ptr() as _,
261 maximum_len,
262 None,
263 )?;
264
265 Ok(cmd_line.to_utf8())
266}