Skip to main content

everything_ipc/
lib.rs

1/*!
2Rust port of voidtools' Everything's IPC SDK.
3Can be used to search user files quickly on Windows.
4
5## Features
6- Support both Everything v1.4 and v1.5, including Alpha version.
7- Higher performance than Everything v1.4's official SDK:
8  - Hot query time is about 30% shorter.
9  - Sending blocking time is 60% shorter for async queries.
10- Support both sync and async (Tokio) querying.
11- Search text generating utilities.
12- Folder-based batch IPC and cache.
13
14## APIs
15- [`IpcWindow`]: A minimal IPC interface for Everything v1.4+.
16- [`wm`]: Everything's window message IPC interface, supported by Everything v1.4+.
17- [`pipe`]: Everything's named pipe IPC interface, supported by Everything v1.5+.
18- [`search`]: Search text generating utilities.
19- [`folder`]: Folder-based batch IPC and cache.
20
21All client types ([`IpcWindow`] and `EverythingClient`) are `Send + Sync`.
22
23## Examples
24```no_run
25// cargo add everything-ipc
26use everything_ipc::wm::{EverythingClient, RequestFlags, Sort};
27
28let everything = EverythingClient::new().expect("not available");
29
30let list = everything
31    .query_wait(r"C:\Windows\ *.exe")
32    .request_flags(RequestFlags::FileName | RequestFlags::Size | RequestFlags::Path)
33    .sort(Sort::SizeDescending)
34    .max_results(5)
35    .call()
36    .expect("query");
37
38println!("Found {} items:", list.len());
39println!("{:<25} {:>10}  {}", "Filename", "Size", "Path");
40for item in list.iter() {
41    // get_string() for String, get_str() for &U16CStr
42    let filename = item.get_string(RequestFlags::FileName).unwrap();
43    let path = item.get_str(RequestFlags::Path).unwrap().display();
44    let size = item.get_size(RequestFlags::Size).unwrap();
45    println!("{:<25} {:>10}  {}", filename, size, path);
46}
47println!("Total: {} items", list.total_len());
48/*
49Found 5 items:
50Filename                        Size  Path
51MRT.exe                    223939376  C:\Windows\System32
52MRT-KB890830.exe           133315992  C:\Windows\System32
53OneDriveSetup.exe           89771848  C:\Windows\WinSxS\amd64_microsoft-windows-onedrive-setup_31bf3856ad364e35_10.0.26100.5074_none_c1340e9ad5f0a5d0
54OneDriveSetup.exe           89771848  C:\Windows\System32
55OneDriveSetup.exe           60357040  C:\Windows\WinSxS\amd64_microsoft-windows-onedrive-setup_31bf3856ad364e35_10.0.26100.1_none_2233e98c8e9ce5f5
56Total: 5742 items
57*/
58```
59*/
60//!
61//! ## Crate features
62#![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        // debug!(?hwnd, ?class_name, "enum_windows_proc");
109        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/**
125An IPC window of Everything.
126
127This struct is a minimal IPC interface for Everything v1.4+.
128It only supports:
129- Detecting Everything.
130- Retrieving its version and instance name.
131- Retrieving its DB status.
132*/
133#[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    /// Find an [`IpcWindow`] by trying common instance names.
144    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    /// Find an [`IpcWindow`] globally using [`FindWindowW`].
152    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        // Null terminator
164        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    /// Find an [`IpcWindow`] from current thread, mainly for plugins.
178    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    /// [`HWND`] of the IPC window.
195    pub fn hwnd(&self) -> HWND {
196        self.hwnd
197    }
198
199    /// Class name of the IPC window.
200    pub fn class_name(&self) -> &str {
201        &self.class_name
202    }
203
204    pub fn instance_name(&self) -> Option<&str> {
205        // e.g. "EVERYTHING_TASKBAR_NOTIFICATION_(1.5a)"
206        self.class_name
207            .strip_prefix("EVERYTHING_TASKBAR_NOTIFICATION_(")
208            .and_then(|s| s.strip_suffix(')'))
209    }
210
211    /// Check if IPC is available
212    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    /// Get the version of Everything
225    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        // const EVERYTHING_IPC_GET_TARGET_MACHINE: u32 = 5;
231
232        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    /// Check if the database is loaded
251    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    /// Check if info is indexed
264    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    /// Convert the version to a tuple of `(major, minor, revision, build)`.
296    pub fn tuple(&self) -> (u32, u32, u32, u32) {
297        (self.major, self.minor, self.revision, self.build)
298    }
299
300    /// Is Everything 1.5 and above.
301    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        // Either exact match or starts with prefix
321        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        // Everything v1.5+ should have major >= 1
339        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        // Test various info types
358        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}