Skip to main content

win_desktop_utils/
lib.rs

1//! Windows desktop helpers for Rust apps.
2//!
3//! `win-desktop-utils` wraps a focused set of Windows desktop chores that many
4//! GUI, tray, installer-adjacent, and local utility apps need, while keeping raw
5//! Win32 shell, shortcut, mutex, known-folder, and elevation APIs out of the
6//! application code that uses them.
7//!
8//! The low-level helpers stay available as small functions, and [`DesktopApp`]
9//! provides a friendlier facade for common app startup and app-data workflows.
10//!
11//! # When To Use This
12//!
13//! Use this crate when a Windows desktop app needs to:
14//!
15//! - [`open_with_default`]
16//! - [`open_with_verb`]
17//! - [`show_properties`]
18//! - [`print_with_default`]
19//! - [`open_url`]
20//! - [`reveal_in_explorer`]
21//! - [`open_containing_folder`]
22//! - [`move_to_recycle_bin`]
23//! - [`move_paths_to_recycle_bin`]
24//! - [`empty_recycle_bin`]
25//! - [`empty_recycle_bin_for_root`]
26//! - [`create_shortcut`]
27//! - [`create_url_shortcut`]
28//! - [`single_instance`]
29//! - [`single_instance_with_scope`]
30//! - [`single_instance_with_options`]
31//! - [`roaming_app_data`]
32//! - [`local_app_data`]
33//! - [`ensure_roaming_app_data`]
34//! - [`ensure_local_app_data`]
35//! - [`is_elevated`]
36//! - [`restart_as_admin`]
37//! - [`run_as_admin`]
38//! - [`run_with_verb`]
39//! - [`InstanceScope`]
40//! - [`SingleInstanceOptions`]
41//! - [`DesktopApp`]
42//! - [`ShortcutOptions`]
43//! - [`ShortcutIcon`]
44//!
45//! This crate supports Windows only.
46//!
47//! # Quick Start
48//!
49//! ```
50//! fn main() -> Result<(), win_desktop_utils::Error> {
51//!     let app = win_desktop_utils::DesktopApp::new(format!(
52//!         "demo-app-{}",
53//!         std::process::id()
54//!     ))?;
55//!
56//!     let _guard = match app.single_instance()? {
57//!         Some(guard) => guard,
58//!         None => {
59//!             println!("already running");
60//!             return Ok(());
61//!         }
62//!     };
63//!
64//!     let local = app.ensure_local_data_dir()?;
65//!     assert!(local.exists());
66//!
67//!     Ok(())
68//! }
69//! ```
70//!
71//! # Feature Flags
72//!
73//! Default features enable the full API. Consumers can opt out and select only
74//! the modules they need:
75//!
76//! ```toml
77//! [dependencies]
78//! win-desktop-utils = { version = "0.4", default-features = false, features = ["paths", "instance"] }
79//! ```
80//!
81//! Available features:
82//!
83//! - `app`: [`DesktopApp`] facade for app-data and single-instance startup.
84//! - `paths`: per-user local and roaming app-data helpers.
85//! - `instance`: named-mutex single-instance helpers.
86//! - `shell`: shell opening, URL, Explorer, and shell-verb helpers.
87//! - `recycle-bin`: Recycle Bin move and empty helpers.
88//! - `shortcuts`: `.lnk` and `.url` shortcut helpers.
89//! - `elevation`: elevation detection and shell-based relaunch helpers.
90//!
91//! The default feature set is intentionally broad for convenience. Feature flags
92//! control this crate's public modules; the underlying `windows` dependency uses
93//! one shared set of Win32 bindings when any Windows API feature is enabled.
94//!
95//! # Common Workflows
96//!
97//! Startup guard plus app-data directory:
98//!
99//! ```
100//! let app = win_desktop_utils::DesktopApp::new(format!(
101//!     "workflow-demo-{}",
102//!     std::process::id()
103//! ))?;
104//! let _guard = app.single_instance()?.expect("first instance");
105//! let config_dir = app.ensure_local_data_dir()?;
106//! assert!(config_dir.exists());
107//! # Ok::<(), win_desktop_utils::Error>(())
108//! ```
109//!
110//! Create a shortcut:
111//!
112//! ```no_run
113//! let shortcut = std::env::current_dir()?.join("notepad.lnk");
114//! let options = win_desktop_utils::ShortcutOptions::new()
115//!     .description("Open Notepad");
116//! win_desktop_utils::create_shortcut(&shortcut, r"C:\Windows\notepad.exe", &options)?;
117//! # Ok::<(), Box<dyn std::error::Error>>(())
118//! ```
119//!
120//! Relaunch the current executable as administrator:
121//!
122//! ```no_run
123//! use std::ffi::OsString;
124//!
125//! if !win_desktop_utils::is_elevated()? {
126//!     win_desktop_utils::restart_as_admin(&[OsString::from("--elevated")])?;
127//! }
128//! # Ok::<(), win_desktop_utils::Error>(())
129//! ```
130//!
131//! # Behavior And Side Effects
132//!
133//! - [`open_with_default`] requires a non-empty existing path.
134//! - [`open_with_verb`] uses `ShellExecuteW` with the requested shell verb.
135//! - [`show_properties`] and [`print_with_default`] are small shell-verb wrappers.
136//! - [`open_url`] trims surrounding whitespace before delegating to the Windows shell.
137//! - [`reveal_in_explorer`] requires an existing path and launches `explorer.exe`.
138//! - [`open_containing_folder`] opens the existing path's parent directory.
139//! - [`move_to_recycle_bin`] requires an absolute existing path and uses `IFileOperation`
140//!   on a dedicated STA thread for recycle-bin behavior.
141//! - [`move_paths_to_recycle_bin`] validates all paths before starting one shell
142//!   recycle-bin operation.
143//! - [`empty_recycle_bin`] permanently empties the Recycle Bin without showing shell UI.
144//! - [`create_shortcut`] uses `IShellLinkW` on a dedicated STA thread.
145//! - [`roaming_app_data`] and [`local_app_data`] resolve their base directories via
146//!   `SHGetKnownFolderPath`.
147//! - [`single_instance`] uses a `Local\...` named mutex, so the lock is scoped to the
148//!   current Windows session, and `app_id` cannot contain backslashes.
149//! - [`single_instance_with_scope`] can opt into either the current-session or global
150//!   mutex namespace.
151//! - [`restart_as_admin`] starts a new elevated instance of the current executable,
152//!   does not terminate the current process, and rejects arguments containing NUL bytes.
153//! - [`run_as_admin`] and [`run_with_verb`] launch arbitrary commands through
154//!   `ShellExecuteW`.
155//!
156//! # Stability
157//!
158//! The minimum supported Rust version is 1.82. Public API compatibility is checked
159//! in CI with `cargo-semver-checks`, and dependency policy is checked with
160//! `cargo-deny`.
161
162#[cfg(not(windows))]
163compile_error!("win-desktop-utils supports Windows only.");
164
165#[cfg(all(windows, feature = "app"))]
166pub mod app;
167#[cfg(all(windows, feature = "elevation"))]
168pub mod elevation;
169#[cfg(windows)]
170pub mod error;
171#[cfg(all(windows, feature = "instance"))]
172pub mod instance;
173#[cfg(all(windows, feature = "paths"))]
174pub mod paths;
175#[cfg(all(windows, any(feature = "shell", feature = "recycle-bin")))]
176pub mod shell;
177#[cfg(all(windows, feature = "shortcuts"))]
178pub mod shortcuts;
179
180#[cfg(windows)]
181pub use error::{Error, Result};
182
183#[cfg(all(windows, feature = "app"))]
184pub use app::DesktopApp;
185#[cfg(all(windows, feature = "elevation"))]
186pub use elevation::{is_elevated, restart_as_admin, run_as_admin, run_with_verb};
187#[cfg(all(windows, feature = "instance"))]
188pub use instance::{
189    single_instance, single_instance_with_options, single_instance_with_scope, InstanceGuard,
190    InstanceScope, SingleInstanceOptions,
191};
192#[cfg(all(windows, feature = "paths"))]
193pub use paths::{ensure_local_app_data, ensure_roaming_app_data, local_app_data, roaming_app_data};
194#[cfg(all(windows, feature = "recycle-bin"))]
195pub use shell::{
196    empty_recycle_bin, empty_recycle_bin_for_root, move_paths_to_recycle_bin, move_to_recycle_bin,
197};
198#[cfg(all(windows, feature = "shell"))]
199pub use shell::{
200    open_containing_folder, open_url, open_with_default, open_with_verb, print_with_default,
201    reveal_in_explorer, show_properties,
202};
203#[cfg(all(windows, feature = "shortcuts"))]
204pub use shortcuts::{create_shortcut, create_url_shortcut, ShortcutIcon, ShortcutOptions};