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.
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
21## Examples
22```no_run
23// cargo add everything-ipc
24use everything_ipc::wm::{EverythingClient, RequestFlags, Sort};
25
26let everything = EverythingClient::new().expect("not available");
27
28let list = everything
29    .query_wait(r"C:\Windows\ *.exe")
30    .request_flags(RequestFlags::FileName | RequestFlags::Size | RequestFlags::Path)
31    .sort(Sort::SizeDescending)
32    .max_results(10)
33    .call()
34    .expect("query");
35
36println!("Found {} items:", list.len());
37println!("{:<25} {:>10}  {}", "Filename", "Size", "Path");
38for item in list.iter() {
39    // get_string() for String, get_str() for &U16CStr
40    let filename = item.get_string(RequestFlags::FileName).unwrap();
41    let path = item.get_str(RequestFlags::Path).unwrap().display();
42    let size = item.get_size(RequestFlags::Size).unwrap();
43    println!("{:<25} {:>10}  {}", filename, size, path);
44}
45println!("Total: {} items", list.total_len());
46/*
47Found 5 items:
48Filename                        Size  Path
49MRT.exe                    223939376  C:\Windows\System32
50MRT-KB890830.exe           133315992  C:\Windows\System32
51OneDriveSetup.exe           89771848  C:\Windows\WinSxS\amd64_microsoft-windows-onedrive-setup_31bf3856ad364e35_10.0.26100.5074_none_c1340e9ad5f0a5d0
52OneDriveSetup.exe           89771848  C:\Windows\System32
53OneDriveSetup.exe           60357040  C:\Windows\WinSxS\amd64_microsoft-windows-onedrive-setup_31bf3856ad364e35_10.0.26100.1_none_2233e98c8e9ce5f5
54Total: 5742 items
55*/
56```
57*/
58//!
59//! ## Crate features
60#![cfg_attr(docsrs, feature(doc_cfg))]
61#![cfg_attr(feature = "doc", doc = document_features::document_features!())]
62
63use ::windows::{
64    Win32::{
65        Foundation::{FALSE, HWND, LPARAM, TRUE, WPARAM},
66        System::Threading::GetCurrentThreadId,
67        UI::WindowsAndMessaging::{
68            EnumThreadWindows, FindWindowW, GetClassNameW, SendMessageW, WM_USER,
69        },
70    },
71    core::{BOOL, PCWSTR},
72};
73use tracing::debug;
74use widestring::{U16Str, u16str};
75
76use crate::wm::{
77    EVERYTHING_IPC_GET_MINOR_VERSION, EVERYTHING_IPC_IS_DB_LOADED,
78    EVERYTHING_IPC_IS_FILE_INFO_INDEXED,
79};
80
81#[cfg(feature = "folder")]
82pub mod folder;
83#[cfg(feature = "tokio")]
84pub mod pipe;
85pub mod search;
86mod windows;
87pub mod wm;
88
89const IPC_CLASS_PREFIX: &U16Str = u16str!("EVERYTHING_TASKBAR_NOTIFICATION");
90
91const EVERYTHING_WM_IPC: u32 = WM_USER;
92
93struct EnumWindowsData {
94    result: Option<IpcWindow>,
95}
96
97unsafe extern "system" fn enum_windows_proc(hwnd: HWND, lparam: LPARAM) -> BOOL {
98    let data = unsafe { &mut *(lparam.0 as *mut EnumWindowsData) };
99
100    let mut buf = [0; 256];
101    let len = unsafe { GetClassNameW(hwnd, &mut buf) };
102    if len > 0 {
103        let class_name = U16Str::from_slice(&buf[..len as usize]);
104        // debug!(?hwnd, ?class_name, "enum_windows_proc");
105        if class_name
106            .as_slice()
107            .starts_with(IPC_CLASS_PREFIX.as_slice())
108        {
109            data.result = Some(IpcWindow {
110                hwnd,
111                class_name: class_name.to_string().unwrap(),
112            });
113            return FALSE;
114        }
115    }
116
117    TRUE
118}
119
120/**
121An IPC window of Everything.
122
123This struct is a minimal IPC interface for Everything v1.4+.
124It only supports:
125- Detecting Everything.
126- Retrieving its version and instance name.
127- Retrieving its DB status.
128*/
129#[derive(Debug, Clone)]
130pub struct IpcWindow {
131    hwnd: HWND,
132    class_name: String,
133}
134
135impl IpcWindow {
136    /// Find an [`IpcWindow`] by trying common instance names.
137    pub fn new() -> Option<Self> {
138        match Self::with_instance(None) {
139            Some(window) => Some(window),
140            None => Self::with_instance(Some("1.5a")),
141        }
142    }
143
144    /// Find an [`IpcWindow`] globally using [`FindWindowW`].
145    pub fn with_instance(instance_name: Option<&str>) -> Option<Self> {
146        let mut class_name = Vec::<u16>::with_capacity(IPC_CLASS_PREFIX.len() + 7);
147        class_name.extend_from_slice(IPC_CLASS_PREFIX.as_slice());
148
149        if let Some(name) = instance_name {
150            class_name.push('(' as u16);
151            for ch in name.encode_utf16() {
152                class_name.push(ch);
153            }
154            class_name.push(')' as u16);
155        }
156        // Null terminator
157        class_name.push(0);
158
159        let hwnd = unsafe { FindWindowW(PCWSTR(class_name.as_ptr()), None).ok() }?;
160        if hwnd.is_invalid() {
161            return None;
162        }
163
164        Some(IpcWindow {
165            hwnd,
166            class_name: String::from_utf16_lossy(&class_name[..class_name.len() - 1]),
167        })
168    }
169
170    /// Find an [`IpcWindow`] from current thread, mainly for plugins.
171    pub fn from_current_thread() -> Option<Self> {
172        let mut data = EnumWindowsData { result: None };
173
174        let tid = unsafe { GetCurrentThreadId() };
175        debug!(?tid, "from_current_thread");
176        _ = unsafe {
177            EnumThreadWindows(
178                tid,
179                Some(enum_windows_proc),
180                LPARAM(&mut data as *mut _ as isize),
181            )
182        };
183
184        data.result
185    }
186
187    /// [`HWND`] of the IPC window.
188    pub fn hwnd(&self) -> HWND {
189        self.hwnd
190    }
191
192    /// Class name of the IPC window.
193    pub fn class_name(&self) -> &str {
194        &self.class_name
195    }
196
197    pub fn instance_name(&self) -> Option<&str> {
198        // e.g. "EVERYTHING_TASKBAR_NOTIFICATION_(1.5a)"
199        self.class_name
200            .strip_prefix("EVERYTHING_TASKBAR_NOTIFICATION_(")
201            .and_then(|s| s.strip_suffix(')'))
202    }
203
204    /// Check if IPC is available
205    pub fn is_ipc_available(&self) -> bool {
206        unsafe {
207            SendMessageW(
208                self.hwnd,
209                EVERYTHING_WM_IPC,
210                Some(WPARAM(EVERYTHING_IPC_GET_MINOR_VERSION as usize)),
211                None,
212            )
213        }
214        .0 >= 4
215    }
216
217    /// Get the version of Everything
218    pub fn get_version(&self) -> Version {
219        const EVERYTHING_IPC_GET_MAJOR_VERSION: u32 = 0;
220        const EVERYTHING_IPC_GET_MINOR_VERSION: u32 = 1;
221        const EVERYTHING_IPC_GET_REVISION: u32 = 2;
222        const EVERYTHING_IPC_GET_BUILD_NUMBER: u32 = 3;
223        // const EVERYTHING_IPC_GET_TARGET_MACHINE: u32 = 5;
224
225        let send_u32 = |command: u32| unsafe {
226            SendMessageW(
227                self.hwnd,
228                EVERYTHING_WM_IPC,
229                Some(WPARAM(command as usize)),
230                None,
231            )
232            .0 as u32
233        };
234
235        Version {
236            major: send_u32(EVERYTHING_IPC_GET_MAJOR_VERSION),
237            minor: send_u32(EVERYTHING_IPC_GET_MINOR_VERSION),
238            revision: send_u32(EVERYTHING_IPC_GET_REVISION),
239            build: send_u32(EVERYTHING_IPC_GET_BUILD_NUMBER),
240        }
241    }
242
243    /// Check if the database is loaded
244    pub fn is_db_loaded(&self) -> bool {
245        unsafe {
246            SendMessageW(
247                self.hwnd,
248                EVERYTHING_WM_IPC,
249                Some(WPARAM(EVERYTHING_IPC_IS_DB_LOADED as usize)),
250                Some(LPARAM(0)),
251            )
252        }
253        .0 != 0
254    }
255
256    /// Check if info is indexed
257    pub fn is_file_info_indexed(&self, info: wm::FileInfo) -> bool {
258        unsafe {
259            SendMessageW(
260                self.hwnd,
261                EVERYTHING_WM_IPC,
262                Some(WPARAM(EVERYTHING_IPC_IS_FILE_INFO_INDEXED as usize)),
263                Some(LPARAM(info as isize)),
264            )
265        }
266        .0 != 0
267    }
268}
269
270#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
271pub struct Version {
272    pub major: u32,
273    pub minor: u32,
274    pub revision: u32,
275    pub build: u32,
276}
277
278impl Version {
279    pub fn new(major: u32, minor: u32, revision: u32, build: u32) -> Self {
280        Self {
281            major,
282            minor,
283            revision,
284            build,
285        }
286    }
287}
288
289#[cfg(test)]
290mod tests {
291    use crate::wm::FileInfo;
292
293    use super::*;
294
295    #[test]
296    fn ipc_window_find_global() {
297        let window = IpcWindow::with_instance(None);
298        assert!(window.is_some(), "Should find Everything IPC window");
299
300        let window = window.unwrap();
301        let class_name = window.class_name();
302        println!("IPC Window class name: {}", class_name);
303        // Either exact match or starts with prefix
304        assert!(
305            class_name == "EVERYTHING_TASKBAR_NOTIFICATION"
306                || class_name.starts_with("EVERYTHING_TASKBAR_NOTIFICATION_(")
307        );
308
309        let version = window.get_version();
310        println!(
311            "Everything version: {}.{}.{}.{}",
312            version.major, version.minor, version.revision, version.build
313        );
314    }
315
316    #[test]
317    fn get_version() {
318        let everything = IpcWindow::new().unwrap();
319        let version = everything.get_version();
320
321        // Everything v1.5+ should have major >= 1
322        assert!(version.major >= 1, "Expected Everything v1+");
323        println!(
324            "Version: {}.{}.{}.{}",
325            version.major, version.minor, version.revision, version.build
326        );
327    }
328
329    #[test]
330    fn is_db_loaded() {
331        let everything = IpcWindow::new().unwrap();
332        let loaded = everything.is_db_loaded();
333        assert!(loaded, "Everything DB should be loaded");
334    }
335
336    #[test]
337    fn is_file_info_indexed() {
338        let everything = IpcWindow::new().unwrap();
339
340        // Test various info types
341        let _ = everything.is_file_info_indexed(FileInfo::FileSize);
342        let _ = everything.is_file_info_indexed(FileInfo::FolderSize);
343        let _ = everything.is_file_info_indexed(FileInfo::DateCreated);
344        let _ = everything.is_file_info_indexed(FileInfo::DateModified);
345        let _ = everything.is_file_info_indexed(FileInfo::DateAccessed);
346        let _ = everything.is_file_info_indexed(FileInfo::Attributes);
347    }
348}