auto_launch/
windows.rs

1use crate::{AutoLaunch, Result};
2use winreg::enums::RegType::REG_BINARY;
3use winreg::enums::{HKEY_CURRENT_USER, KEY_READ, KEY_SET_VALUE};
4use winreg::{RegKey, RegValue};
5
6static AL_REGKEY: &str = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
7static TASK_MANAGER_OVERRIDE_REGKEY: &str =
8    "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\Run";
9static TASK_MANAGER_OVERRIDE_ENABLED_VALUE: [u8; 12] = [
10    0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11];
12
13/// Windows implement
14impl AutoLaunch {
15    /// Create a new AutoLaunch instance
16    /// - `app_name`: application name
17    /// - `app_path`: application path
18    /// - `args`: startup args passed to the binary
19    ///
20    /// ## Notes
21    ///
22    /// The parameters of `AutoLaunch::new` are different on each platform.
23    pub fn new(app_name: &str, app_path: &str, args: &[impl AsRef<str>]) -> AutoLaunch {
24        AutoLaunch {
25            app_name: app_name.into(),
26            app_path: app_path.into(),
27            args: args.iter().map(|s| s.as_ref().to_string()).collect(),
28        }
29    }
30
31    /// Enable the AutoLaunch setting
32    ///
33    /// ## Errors
34    ///
35    /// - failed to open the registry key
36    /// - failed to set value
37    pub fn enable(&self) -> Result<()> {
38        let hkcu = RegKey::predef(HKEY_CURRENT_USER);
39        hkcu.open_subkey_with_flags(AL_REGKEY, KEY_SET_VALUE)?
40            .set_value::<_, _>(
41                &self.app_name,
42                &format!("{} {}", &self.app_path, &self.args.join(" ")),
43            )?;
44
45        // this key maybe not found
46        if let Ok(reg) = hkcu.open_subkey_with_flags(TASK_MANAGER_OVERRIDE_REGKEY, KEY_SET_VALUE) {
47            reg.set_raw_value(
48                &self.app_name,
49                &RegValue {
50                    vtype: REG_BINARY,
51                    bytes: TASK_MANAGER_OVERRIDE_ENABLED_VALUE.to_vec(),
52                },
53            )?;
54        }
55
56        Ok(())
57    }
58
59    /// Disable the AutoLaunch setting
60    ///
61    /// ## Errors
62    ///
63    /// - failed to open the registry key
64    /// - failed to delete value
65    pub fn disable(&self) -> Result<()> {
66        let hkcu = RegKey::predef(HKEY_CURRENT_USER);
67        hkcu.open_subkey_with_flags(AL_REGKEY, KEY_SET_VALUE)?
68            .delete_value(&self.app_name)?;
69        Ok(())
70    }
71
72    /// Check whether the AutoLaunch setting is enabled
73    pub fn is_enabled(&self) -> Result<bool> {
74        let hkcu = RegKey::predef(HKEY_CURRENT_USER);
75
76        let al_enabled = hkcu
77            .open_subkey_with_flags(AL_REGKEY, KEY_READ)?
78            .get_value::<String, _>(&self.app_name)
79            .is_ok();
80        let task_manager_enabled = self.task_manager_enabled(hkcu);
81
82        Ok(al_enabled && task_manager_enabled.unwrap_or(true))
83    }
84
85    fn task_manager_enabled(&self, hkcu: RegKey) -> Option<bool> {
86        let task_manager_override_raw_value = hkcu
87            .open_subkey_with_flags(TASK_MANAGER_OVERRIDE_REGKEY, KEY_READ)
88            .ok()?
89            .get_raw_value(&self.app_name)
90            .ok()?;
91        Some(last_eight_bytes_all_zeros(
92            &task_manager_override_raw_value.bytes,
93        )?)
94    }
95}
96
97fn last_eight_bytes_all_zeros(bytes: &[u8]) -> Option<bool> {
98    if bytes.len() < 8 {
99        return None;
100    }
101    Some(bytes.iter().rev().take(8).all(|v| *v == 0u8))
102}