use std::fmt;
use winreg::enums::*;
use winreg::RegKey;
const RUN_KEY_CU: &str = r"Software\Microsoft\Windows\CurrentVersion\Run";
const RUN_KEY_LM: &str = r"Software\Microsoft\Windows\CurrentVersion\Run";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StartupScope {
CurrentUser,
LocalMachine,
}
impl fmt::Display for StartupScope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StartupScope::CurrentUser => write!(f, "HKCU"),
StartupScope::LocalMachine => write!(f, "HKLM"),
}
}
}
impl StartupScope {
fn open_run_key(&self) -> Result<RegKey, String> {
match self {
StartupScope::CurrentUser => RegKey::predef(HKEY_CURRENT_USER)
.open_subkey_with_flags(RUN_KEY_CU, KEY_READ | KEY_WRITE),
StartupScope::LocalMachine => RegKey::predef(HKEY_LOCAL_MACHINE)
.open_subkey_with_flags(RUN_KEY_LM, KEY_READ | KEY_WRITE),
}
.map_err(|e| format!("failed to open registry key [{}]: {}", self, e))
}
fn open_run_key_readonly(&self) -> Result<RegKey, String> {
match self {
StartupScope::CurrentUser => RegKey::predef(HKEY_CURRENT_USER)
.open_subkey_with_flags(RUN_KEY_CU, KEY_READ),
StartupScope::LocalMachine => RegKey::predef(HKEY_LOCAL_MACHINE)
.open_subkey_with_flags(RUN_KEY_LM, KEY_READ),
}
.map_err(|e| format!("failed to open registry key [{}]: {}", self, e))
}
}
#[derive(Debug, Clone)]
pub struct StartupEntry {
pub name: String,
pub command: String,
pub scope: StartupScope,
}
pub fn add(name: &str, command: &str, scope: StartupScope) -> Result<(), String> {
let key = scope.open_run_key()?;
key.set_value(name, &command)
.map_err(|e| format!("failed to add startup entry '{}': {}", name, e))?;
Ok(())
}
pub fn remove(name: &str, scope: StartupScope) -> Result<(), String> {
let key = scope.open_run_key()?;
match key.delete_value(name) {
Ok(()) => Ok(()),
Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
Err(e) => Err(format!("failed to remove startup entry '{}': {}", name, e)),
}
}
pub fn exists(name: &str, scope: StartupScope) -> Result<bool, String> {
let key = scope.open_run_key_readonly()?;
match key.get_value::<String, _>(name) {
Ok(_) => Ok(true),
Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false),
Err(e) => Err(format!("failed to query startup entry '{}': {}", name, e)),
}
}
pub fn list(scope: StartupScope) -> Result<Vec<StartupEntry>, String> {
let key = scope.open_run_key_readonly()?;
let mut entries = Vec::new();
for name_result in key.enum_values() {
let (name, value) = name_result
.map_err(|e| format!("failed to enumerate startup entries: {}", e))?;
let command = match value {
winreg::RegValue {
vtype: REG_SZ | REG_EXPAND_SZ,
bytes,
} => String::from_utf16(
&bytes
.chunks_exact(2)
.map(|b| u16::from_le_bytes([b[0], b[1]]))
.collect::<Vec<_>>(),
)
.unwrap_or_else(|_| String::from_utf8_lossy(&bytes).into_owned()),
_ => continue, };
entries.push(StartupEntry {
name,
command,
scope,
});
}
Ok(entries)
}
pub fn list_all() -> Result<Vec<StartupEntry>, String> {
let mut all = Vec::new();
for scope in [StartupScope::CurrentUser, StartupScope::LocalMachine] {
match list(scope) {
Ok(mut entries) => all.append(&mut entries),
Err(_) if scope == StartupScope::LocalMachine => {}
Err(e) => return Err(e),
}
}
Ok(all)
}