1#![cfg_attr(docsrs, feature(doc_cfg))]
63#![cfg_attr(feature = "doc", doc = document_features::document_features!())]
64
65use ::windows::{
66 Win32::{
67 Foundation::{FALSE, HWND, LPARAM, TRUE, WPARAM},
68 System::Threading::GetCurrentThreadId,
69 UI::WindowsAndMessaging::{
70 EnumThreadWindows, FindWindowW, GetClassNameW, SendMessageW, WM_USER,
71 },
72 },
73 core::{BOOL, PCWSTR},
74};
75use tracing::debug;
76use widestring::{U16Str, u16str};
77
78use crate::wm::{
79 EVERYTHING_IPC_GET_MINOR_VERSION, EVERYTHING_IPC_IS_DB_LOADED,
80 EVERYTHING_IPC_IS_FILE_INFO_INDEXED,
81};
82
83#[cfg(feature = "folder")]
84pub mod folder;
85#[cfg(feature = "pe")]
86pub mod pe;
87#[cfg(feature = "tokio")]
88pub mod pipe;
89pub mod search;
90mod windows;
91pub mod wm;
92
93const IPC_CLASS_PREFIX: &U16Str = u16str!("EVERYTHING_TASKBAR_NOTIFICATION");
94
95const EVERYTHING_WM_IPC: u32 = WM_USER;
96
97struct EnumWindowsData {
98 result: Option<IpcWindow>,
99}
100
101unsafe extern "system" fn enum_windows_proc(hwnd: HWND, lparam: LPARAM) -> BOOL {
102 let data = unsafe { &mut *(lparam.0 as *mut EnumWindowsData) };
103
104 let mut buf = [0; 256];
105 let len = unsafe { GetClassNameW(hwnd, &mut buf) };
106 if len > 0 {
107 let class_name = U16Str::from_slice(&buf[..len as usize]);
108 if class_name
110 .as_slice()
111 .starts_with(IPC_CLASS_PREFIX.as_slice())
112 {
113 data.result = Some(IpcWindow {
114 hwnd,
115 class_name: class_name.to_string().unwrap(),
116 });
117 return FALSE;
118 }
119 }
120
121 TRUE
122}
123
124#[derive(Debug, Clone)]
134pub struct IpcWindow {
135 hwnd: HWND,
136 class_name: String,
137}
138
139unsafe impl Send for IpcWindow {}
140unsafe impl Sync for IpcWindow {}
141
142impl IpcWindow {
143 pub fn new() -> Option<Self> {
145 match Self::with_instance(None) {
146 Some(window) => Some(window),
147 None => Self::with_instance(Some("1.5a")),
148 }
149 }
150
151 pub fn with_instance(instance_name: Option<&str>) -> Option<Self> {
153 let mut class_name = Vec::<u16>::with_capacity(IPC_CLASS_PREFIX.len() + 7);
154 class_name.extend_from_slice(IPC_CLASS_PREFIX.as_slice());
155
156 if let Some(name) = instance_name {
157 class_name.push('(' as u16);
158 for ch in name.encode_utf16() {
159 class_name.push(ch);
160 }
161 class_name.push(')' as u16);
162 }
163 class_name.push(0);
165
166 let hwnd = unsafe { FindWindowW(PCWSTR(class_name.as_ptr()), None).ok() }?;
167 if hwnd.is_invalid() {
168 return None;
169 }
170
171 Some(IpcWindow {
172 hwnd,
173 class_name: String::from_utf16_lossy(&class_name[..class_name.len() - 1]),
174 })
175 }
176
177 pub fn from_current_thread() -> Option<Self> {
179 let mut data = EnumWindowsData { result: None };
180
181 let tid = unsafe { GetCurrentThreadId() };
182 debug!(?tid, "from_current_thread");
183 _ = unsafe {
184 EnumThreadWindows(
185 tid,
186 Some(enum_windows_proc),
187 LPARAM(&mut data as *mut _ as isize),
188 )
189 };
190
191 data.result
192 }
193
194 pub fn hwnd(&self) -> HWND {
196 self.hwnd
197 }
198
199 pub fn class_name(&self) -> &str {
201 &self.class_name
202 }
203
204 pub fn instance_name(&self) -> Option<&str> {
205 self.class_name
207 .strip_prefix("EVERYTHING_TASKBAR_NOTIFICATION_(")
208 .and_then(|s| s.strip_suffix(')'))
209 }
210
211 pub fn is_ipc_available(&self) -> bool {
213 unsafe {
214 SendMessageW(
215 self.hwnd,
216 EVERYTHING_WM_IPC,
217 Some(WPARAM(EVERYTHING_IPC_GET_MINOR_VERSION as usize)),
218 None,
219 )
220 }
221 .0 >= 4
222 }
223
224 pub fn get_version(&self) -> Version {
226 const EVERYTHING_IPC_GET_MAJOR_VERSION: u32 = 0;
227 const EVERYTHING_IPC_GET_MINOR_VERSION: u32 = 1;
228 const EVERYTHING_IPC_GET_REVISION: u32 = 2;
229 const EVERYTHING_IPC_GET_BUILD_NUMBER: u32 = 3;
230 let send_u32 = |command: u32| unsafe {
233 SendMessageW(
234 self.hwnd,
235 EVERYTHING_WM_IPC,
236 Some(WPARAM(command as usize)),
237 None,
238 )
239 .0 as u32
240 };
241
242 Version {
243 major: send_u32(EVERYTHING_IPC_GET_MAJOR_VERSION),
244 minor: send_u32(EVERYTHING_IPC_GET_MINOR_VERSION),
245 revision: send_u32(EVERYTHING_IPC_GET_REVISION),
246 build: send_u32(EVERYTHING_IPC_GET_BUILD_NUMBER),
247 }
248 }
249
250 pub fn is_db_loaded(&self) -> bool {
252 unsafe {
253 SendMessageW(
254 self.hwnd,
255 EVERYTHING_WM_IPC,
256 Some(WPARAM(EVERYTHING_IPC_IS_DB_LOADED as usize)),
257 Some(LPARAM(0)),
258 )
259 }
260 .0 != 0
261 }
262
263 pub fn is_file_info_indexed(&self, info: wm::FileInfo) -> bool {
265 unsafe {
266 SendMessageW(
267 self.hwnd,
268 EVERYTHING_WM_IPC,
269 Some(WPARAM(EVERYTHING_IPC_IS_FILE_INFO_INDEXED as usize)),
270 Some(LPARAM(info as isize)),
271 )
272 }
273 .0 != 0
274 }
275}
276
277#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
278pub struct Version {
279 pub major: u32,
280 pub minor: u32,
281 pub revision: u32,
282 pub build: u32,
283}
284
285impl Version {
286 pub fn new(major: u32, minor: u32, revision: u32, build: u32) -> Self {
287 Self {
288 major,
289 minor,
290 revision,
291 build,
292 }
293 }
294
295 pub fn tuple(&self) -> (u32, u32, u32, u32) {
297 (self.major, self.minor, self.revision, self.build)
298 }
299
300 pub fn ge_15(&self) -> bool {
302 self.tuple() >= (1, 5, 0, 0)
303 }
304}
305
306#[cfg(test)]
307mod tests {
308 use crate::wm::FileInfo;
309
310 use super::*;
311
312 #[test]
313 fn ipc_window_find_global() {
314 let window = IpcWindow::with_instance(None);
315 assert!(window.is_some(), "Should find Everything IPC window");
316
317 let window = window.unwrap();
318 let class_name = window.class_name();
319 println!("IPC Window class name: {}", class_name);
320 assert!(
322 class_name == "EVERYTHING_TASKBAR_NOTIFICATION"
323 || class_name.starts_with("EVERYTHING_TASKBAR_NOTIFICATION_(")
324 );
325
326 let version = window.get_version();
327 println!(
328 "Everything version: {}.{}.{}.{}",
329 version.major, version.minor, version.revision, version.build
330 );
331 }
332
333 #[test]
334 fn get_version() {
335 let everything = IpcWindow::new().unwrap();
336 let version = everything.get_version();
337
338 assert!(version.major >= 1, "Expected Everything v1+");
340 println!(
341 "Version: {}.{}.{}.{}",
342 version.major, version.minor, version.revision, version.build
343 );
344 }
345
346 #[test]
347 fn is_db_loaded() {
348 let everything = IpcWindow::new().unwrap();
349 let loaded = everything.is_db_loaded();
350 assert!(loaded, "Everything DB should be loaded");
351 }
352
353 #[test]
354 fn is_file_info_indexed() {
355 let everything = IpcWindow::new().unwrap();
356
357 let _ = everything.is_file_info_indexed(FileInfo::FileSize);
359 let _ = everything.is_file_info_indexed(FileInfo::FolderSize);
360 let _ = everything.is_file_info_indexed(FileInfo::DateCreated);
361 let _ = everything.is_file_info_indexed(FileInfo::DateModified);
362 let _ = everything.is_file_info_indexed(FileInfo::DateAccessed);
363 let _ = everything.is_file_info_indexed(FileInfo::Attributes);
364 }
365}