winctx/
autostart.rs

1use std::env::current_exe;
2use std::ffi::{OsStr, OsString};
3use std::io;
4use std::path::Path;
5
6use crate::convert::encode_escaped_os_str;
7use crate::error::Error;
8use crate::error::ErrorKind::*;
9use crate::registry::OpenRegistryKey;
10use crate::Result;
11
12/// Helper to register and qeury for a binary to autostart.
13#[non_exhaustive]
14pub struct AutoStart {
15    name: Box<OsStr>,
16    executable: Box<Path>,
17    arguments: Vec<OsString>,
18}
19
20impl AutoStart {
21    /// Helper to make the current executable automatically start.
22    pub fn current_exe<N>(name: N) -> Result<Self>
23    where
24        N: AsRef<OsStr>,
25    {
26        let executable = current_exe().map_err(CurrentExecutable)?;
27        Ok(Self::new(name, executable))
28    }
29
30    /// Construct a new auto start helper.
31    ///
32    /// The name should be something suitable for a registry key, like
33    /// `OxidizeBot`. Note that in the registry it is case-insensitive.
34    #[inline]
35    pub fn new<N, E>(name: N, executable: E) -> Self
36    where
37        N: AsRef<OsStr>,
38        E: AsRef<Path>,
39    {
40        Self {
41            name: name.as_ref().into(),
42            executable: executable.as_ref().into(),
43            arguments: Vec::new(),
44        }
45    }
46
47    /// Append arguments to the executable when autostarting.
48    pub fn arguments<A>(&mut self, arguments: A)
49    where
50        A: IntoIterator,
51        A::Item: AsRef<OsStr>,
52    {
53        self.arguments = arguments
54            .into_iter()
55            .map(|a| a.as_ref().to_os_string())
56            .collect();
57    }
58}
59
60impl AutoStart {
61    /// Entry for automatic startup.
62    fn registry_entry(&self) -> Result<String> {
63        let mut entry = String::new();
64
65        encode_escaped_os_str(&mut entry, self.executable.as_os_str())
66            .map_err(BadAutoStartExecutable)?;
67
68        for argument in &self.arguments {
69            entry.push(' ');
70            encode_escaped_os_str(&mut entry, argument).map_err(BadAutoStartArgument)?;
71        }
72
73        Ok(entry)
74    }
75
76    /// If the program is installed to run at startup.
77    pub fn is_installed(&self) -> Result<bool> {
78        let key = OpenRegistryKey::current_user()
79            .open("Software\\Microsoft\\Windows\\CurrentVersion\\Run")
80            .map_err(OpenRegistryKey)?;
81
82        let path = match key.get_string(&self.name) {
83            Ok(path) => path,
84            Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(false),
85            Err(e) => return Err(Error::new(GetRegistryValue(e))),
86        };
87
88        Ok(self.registry_entry()?.as_str() == path)
89    }
90
91    /// Install the current executable to be automatically started.
92    pub fn install(&self) -> Result<()> {
93        let key = OpenRegistryKey::current_user()
94            .set_value()
95            .open("Software\\Microsoft\\Windows\\CurrentVersion\\Run")
96            .map_err(OpenRegistryKey)?;
97        key.set(&self.name, self.registry_entry()?)
98            .map_err(SetRegistryKey)?;
99        Ok(())
100    }
101
102    /// Remove the program from automatic startup.
103    pub fn uninstall(&self) -> Result<()> {
104        let key = OpenRegistryKey::current_user()
105            .set_value()
106            .open("Software\\Microsoft\\Windows\\CurrentVersion\\Run")
107            .map_err(OpenRegistryKey)?;
108        key.delete(&self.name).map_err(DeleteRegistryKey)?;
109        Ok(())
110    }
111}