Skip to content

Commit b5c700a

Browse files
authored
feat: add Windows enable mode (#21)
Currently we try to enable the auto launch dynamically (system-wide first if we have admin privileges, with a fallback to current user). In some cases it might make sense to configure this behavior though - for instance when the app is installed to the current user only, and you want to avoid the local machine registry.
1 parent 6542a3c commit b5c700a

File tree

2 files changed

+93
-30
lines changed

2 files changed

+93
-30
lines changed

src/lib.rs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,12 @@
6262
//!
6363
//! ### Windows
6464
//!
65-
//! On Windows, it will add a registry entry under `\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run`.
65+
//! On Windows, it will add a registry entry under either `\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run` (system-wide) or
66+
//! `\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run` (current user only).
67+
//!
68+
//! By default we try to apply the auto launch to the system registry, which requires admin privileges and applies the auto launch to any user in the system.
69+
//! If there's no permission to do so, we fallback to enabling it to the current user only.
70+
//! To change this behavior, you can use [`AutoLaunch::set_windows_enable_mode`].
6671
//!
6772
//! ```rust
6873
//! # #[cfg(target_os = "windows")]
@@ -192,6 +197,9 @@ pub struct AutoLaunch {
192197
/// Whether use Launch Agent for implement or use AppleScript
193198
pub(crate) use_launch_agent: bool,
194199

200+
#[cfg(windows)]
201+
pub(crate) enable_mode: WindowsEnableMode,
202+
195203
/// Args passed to the binary on startup
196204
pub(crate) args: Vec<String>,
197205
}
@@ -266,9 +274,29 @@ pub struct AutoLaunchBuilder {
266274

267275
pub use_launch_agent: bool,
268276

277+
pub windows_enable_mode: WindowsEnableMode,
278+
269279
pub args: Option<Vec<String>>,
270280
}
271281

282+
/// Determines how the auto launch is enabled on Windows.
283+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
284+
pub enum WindowsEnableMode {
285+
/// Dynamically tries to enable the auto launch for the system (admin privileges required),
286+
/// fallbacks to the current user if there is no permission to modify the system registry.
287+
Dynamic,
288+
/// Enables the auto launch for the current user only. Does not require admin permissions.
289+
CurrentUser,
290+
/// Enables the auto launch for all users. Requires admin permissions.
291+
System,
292+
}
293+
294+
impl Default for WindowsEnableMode {
295+
fn default() -> Self {
296+
Self::Dynamic
297+
}
298+
}
299+
272300
impl AutoLaunchBuilder {
273301
pub fn new() -> AutoLaunchBuilder {
274302
AutoLaunchBuilder::default()
@@ -293,6 +321,13 @@ impl AutoLaunchBuilder {
293321
self
294322
}
295323

324+
/// Set the [`WindowsEnableMode`].
325+
/// This setting only works on Windows
326+
pub fn set_windows_enable_mode(&mut self, mode: WindowsEnableMode) -> &mut Self {
327+
self.windows_enable_mode = mode;
328+
self
329+
}
330+
296331
/// Set the args
297332
pub fn set_args(&mut self, args: &[impl AsRef<str>]) -> &mut Self {
298333
self.args = Some(args.iter().map(|s| s.as_ref().to_string()).collect());
@@ -324,7 +359,12 @@ impl AutoLaunchBuilder {
324359
&args,
325360
));
326361
#[cfg(target_os = "windows")]
327-
return Ok(AutoLaunch::new(app_name, app_path, &args));
362+
return Ok(AutoLaunch::new(
363+
app_name,
364+
app_path,
365+
self.windows_enable_mode,
366+
&args,
367+
));
328368

329369
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
330370
return Err(Error::UnsupportedOS);

src/windows.rs

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{AutoLaunch, Result};
1+
use crate::{AutoLaunch, Result, WindowsEnableMode};
22
use windows_registry::{Key, CURRENT_USER, LOCAL_MACHINE};
33
use windows_result::HRESULT;
44

@@ -19,15 +19,22 @@ impl AutoLaunch {
1919
/// Create a new AutoLaunch instance
2020
/// - `app_name`: application name
2121
/// - `app_path`: application path
22+
/// - `enable_mode`: behavior of the enable feature
2223
/// - `args`: startup args passed to the binary
2324
///
2425
/// ## Notes
2526
///
2627
/// The parameters of `AutoLaunch::new` are different on each platform.
27-
pub fn new(app_name: &str, app_path: &str, args: &[impl AsRef<str>]) -> AutoLaunch {
28+
pub fn new(
29+
app_name: &str,
30+
app_path: &str,
31+
enable_mode: WindowsEnableMode,
32+
args: &[impl AsRef<str>],
33+
) -> AutoLaunch {
2834
AutoLaunch {
2935
app_name: app_name.into(),
3036
app_path: app_path.into(),
37+
enable_mode,
3138
args: args.iter().map(|s| s.as_ref().to_string()).collect(),
3239
}
3340
}
@@ -39,15 +46,22 @@ impl AutoLaunch {
3946
/// - failed to open the registry key
4047
/// - failed to set value
4148
pub fn enable(&self) -> Result<()> {
42-
self.enable_as_admin()
43-
.or_else(|e| {
44-
if e.code() == E_ACCESSDENIED {
45-
self.enable_as_current_user()
46-
} else {
47-
Err(e)
48-
}
49-
})
50-
.map_err(std::io::Error::from)?;
49+
match self.enable_mode {
50+
WindowsEnableMode::Dynamic => self
51+
.enable_as_admin()
52+
.or_else(|e| {
53+
if e.code() == E_ACCESSDENIED {
54+
self.enable_as_current_user()
55+
} else {
56+
Err(e)
57+
}
58+
})
59+
.map_err(std::io::Error::from)?,
60+
WindowsEnableMode::CurrentUser => self
61+
.enable_as_current_user()
62+
.map_err(std::io::Error::from)?,
63+
WindowsEnableMode::System => self.enable_as_admin().map_err(std::io::Error::from)?,
64+
}
5165
Ok(())
5266
}
5367

@@ -90,16 +104,21 @@ impl AutoLaunch {
90104
/// - failed to open the registry key
91105
/// - failed to delete value
92106
pub fn disable(&self) -> Result<()> {
93-
self.disable_as_admin()
94-
.or_else(|e| {
95-
if e.code() == E_ACCESSDENIED {
96-
self.disable_as_current_user()
97-
} else {
98-
Err(e)
99-
}
100-
})
101-
.map_err(std::io::Error::from)?;
102-
Ok(())
107+
// try to delete both admin and current user registry values
108+
match self.disable_as_admin() {
109+
Ok(()) => {
110+
// try to delete for current user aswell, ignoring errors
111+
// this is useful in case the app was previously registered as a system-wide auto launch
112+
// but changed to a current user only mode
113+
let _ = self.disable_as_current_user();
114+
Ok(())
115+
}
116+
Err(_e) => {
117+
self.disable_as_current_user()
118+
.map_err(std::io::Error::from)?;
119+
Ok(())
120+
}
121+
}
103122
}
104123

105124
fn disable_as_admin(&self) -> windows_registry::Result<()> {
@@ -118,14 +137,18 @@ impl AutoLaunch {
118137

119138
/// Check whether the AutoLaunch setting is enabled
120139
pub fn is_enabled(&self) -> Result<bool> {
121-
let res = match self.is_enabled_as_admin() {
122-
Ok(false) => self.is_enabled_as_current_user(),
123-
Err(e) if e.code() == E_ACCESSDENIED => self.is_enabled_as_current_user(),
124-
Ok(enabled) => Ok(enabled),
125-
Err(e) => Err(e),
140+
match self.enable_mode {
141+
WindowsEnableMode::Dynamic => match self.is_enabled_as_admin() {
142+
Ok(false) => self.is_enabled_as_current_user(),
143+
Err(e) if e.code() == E_ACCESSDENIED => self.is_enabled_as_current_user(),
144+
Ok(enabled) => Ok(enabled),
145+
Err(e) => Err(e),
146+
},
147+
WindowsEnableMode::CurrentUser => self.is_enabled_as_current_user(),
148+
WindowsEnableMode::System => self.is_enabled_as_admin(),
126149
}
127-
.map_err(std::io::Error::from)?;
128-
Ok(res)
150+
.map_err(std::io::Error::from)
151+
.map_err(Into::into)
129152
}
130153

131154
fn is_enabled_as_admin(&self) -> windows_registry::Result<bool> {

0 commit comments

Comments
 (0)