From 41629df1893c40f82b175cf59a66fd387ce49ede Mon Sep 17 00:00:00 2001 From: wonfen Date: Sat, 19 Apr 2025 13:51:11 +0800 Subject: [PATCH] feat: autolaunch via Startup folder on windows --- UPDATELOG.md | 1 + src-tauri/src/core/sysopt.rs | 61 +++++++++++++++-- src-tauri/src/utils/autostart.rs | 112 +++++++++++++++++++++++++++++++ src-tauri/src/utils/mod.rs | 1 + 4 files changed, 168 insertions(+), 7 deletions(-) create mode 100644 src-tauri/src/utils/autostart.rs diff --git a/UPDATELOG.md b/UPDATELOG.md index 0d502d32..fae1af7e 100644 --- a/UPDATELOG.md +++ b/UPDATELOG.md @@ -19,6 +19,7 @@ #### 优化了: - 系统代理 Bypass 设置 + - Windows 下使用 Startup 文件夹的方式实现开机自启,解决管理员模式下开机自启的各种问题 ## v2.2.3 diff --git a/src-tauri/src/core/sysopt.rs b/src-tauri/src/core/sysopt.rs index c727a1cb..54a880b7 100644 --- a/src-tauri/src/core/sysopt.rs +++ b/src-tauri/src/core/sysopt.rs @@ -1,3 +1,4 @@ +use crate::utils::autostart as startup_shortcut; use crate::{ config::{Config, IVerge}, core::handle::Handle, @@ -219,22 +220,68 @@ impl Sysopt { /// update the startup pub fn update_launch(&self) -> Result<()> { let enable_auto_launch = { Config::verge().latest().enable_auto_launch }; - let app_handle = Handle::global().app_handle().unwrap(); - let autostart_manager = app_handle.autolaunch(); - let is_enable = enable_auto_launch.unwrap_or(false); logging!(info, true, "Setting auto-launch state to: {:?}", is_enable); - if is_enable { - logging_error!(Type::System, true, "{:?}", autostart_manager.enable()); - } else { - logging_error!(Type::System, true, "{:?}", autostart_manager.disable()); + + // 首先尝试使用快捷方式方法 + #[cfg(target_os = "windows")] + { + if is_enable { + if let Err(e) = startup_shortcut::create_shortcut() { + log::error!(target: "app", "创建启动快捷方式失败: {}", e); + // 如果快捷方式创建失败,回退到原来的方法 + self.try_original_autostart_method(is_enable); + } else { + return Ok(()); + } + } else { + if let Err(e) = startup_shortcut::remove_shortcut() { + log::error!(target: "app", "删除启动快捷方式失败: {}", e); + self.try_original_autostart_method(is_enable); + } else { + return Ok(()); + } + } + } + + #[cfg(not(target_os = "windows"))] + { + // 非Windows平台使用原来的方法 + self.try_original_autostart_method(is_enable); } Ok(()) } + /// 尝试使用原来的自启动方法 + fn try_original_autostart_method(&self, is_enable: bool) { + let app_handle = Handle::global().app_handle().unwrap(); + let autostart_manager = app_handle.autolaunch(); + + if is_enable { + logging_error!(Type::System, true, "{:?}", autostart_manager.enable()); + } else { + logging_error!(Type::System, true, "{:?}", autostart_manager.disable()); + } + } + /// 获取当前自启动的实际状态 pub fn get_launch_status(&self) -> Result { + // 首先尝试检查快捷方式是否存在 + #[cfg(target_os = "windows")] + { + match startup_shortcut::is_shortcut_enabled() { + Ok(enabled) => { + log::info!(target: "app", "快捷方式自启动状态: {}", enabled); + return Ok(enabled); + } + Err(e) => { + log::error!(target: "app", "检查快捷方式失败,尝试原来的方法: {}", e); + } + } + } + + // 回退到原来的方法 let app_handle = Handle::global().app_handle().unwrap(); let autostart_manager = app_handle.autolaunch(); diff --git a/src-tauri/src/utils/autostart.rs b/src-tauri/src/utils/autostart.rs new file mode 100644 index 00000000..e76996ff --- /dev/null +++ b/src-tauri/src/utils/autostart.rs @@ -0,0 +1,112 @@ +use anyhow::{anyhow, Result}; +use log::info; +use std::fs; +use std::path::{Path, PathBuf}; + +/// Windows 下的开机启动文件夹路径 +#[cfg(target_os = "windows")] +pub fn get_startup_dir() -> Result { + let appdata = std::env::var("APPDATA").map_err(|_| anyhow!("无法获取 APPDATA 环境变量"))?; + + let startup_dir = Path::new(&appdata) + .join("Microsoft") + .join("Windows") + .join("Start Menu") + .join("Programs") + .join("Startup"); + + if !startup_dir.exists() { + return Err(anyhow!("Startup 目录不存在: {:?}", startup_dir)); + } + + Ok(startup_dir) +} + +/// 获取当前可执行文件路径 +pub fn get_exe_path() -> Result { + let exe_path = + std::env::current_exe().map_err(|e| anyhow!("无法获取当前可执行文件路径: {}", e))?; + + Ok(exe_path) +} + +/// 创建快捷方式 +#[cfg(target_os = "windows")] +pub fn create_shortcut() -> Result<()> { + let exe_path = get_exe_path()?; + let startup_dir = get_startup_dir()?; + let shortcut_path = startup_dir.join("Clash-Verge.lnk"); + + // 如果快捷方式已存在,直接返回成功 + if shortcut_path.exists() { + info!(target: "app", "启动快捷方式已存在"); + return Ok(()); + } + + // 使用 PowerShell 创建快捷方式 + let powershell_command = format!( + "$WshShell = New-Object -ComObject WScript.Shell; \ + $Shortcut = $WshShell.CreateShortcut('{}'); \ + $Shortcut.TargetPath = '{}'; \ + $Shortcut.Save()", + shortcut_path.to_string_lossy().replace("\\", "\\\\"), + exe_path.to_string_lossy().replace("\\", "\\\\") + ); + + let output = std::process::Command::new("powershell") + .args(["-Command", &powershell_command]) + .output() + .map_err(|e| anyhow!("执行 PowerShell 命令失败: {}", e))?; + + if !output.status.success() { + let error_msg = String::from_utf8_lossy(&output.stderr); + return Err(anyhow!("创建快捷方式失败: {}", error_msg)); + } + + info!(target: "app", "成功创建启动快捷方式"); + Ok(()) +} + +/// 删除快捷方式 +#[cfg(target_os = "windows")] +pub fn remove_shortcut() -> Result<()> { + let startup_dir = get_startup_dir()?; + let shortcut_path = startup_dir.join("Clash-Verge.lnk"); + + // 如果快捷方式不存在,直接返回成功 + if !shortcut_path.exists() { + info!(target: "app", "启动快捷方式不存在,无需删除"); + return Ok(()); + } + + // 删除快捷方式 + fs::remove_file(&shortcut_path).map_err(|e| anyhow!("删除快捷方式失败: {}", e))?; + + info!(target: "app", "成功删除启动快捷方式"); + Ok(()) +} + +/// 检查快捷方式是否存在 +#[cfg(target_os = "windows")] +pub fn is_shortcut_enabled() -> Result { + let startup_dir = get_startup_dir()?; + let shortcut_path = startup_dir.join("Clash-Verge.lnk"); + + Ok(shortcut_path.exists()) +} + +// 非 Windows 平台使用的空方法 +#[cfg(not(target_os = "windows"))] +pub fn create_shortcut() -> Result<()> { + Ok(()) +} + +#[cfg(not(target_os = "windows"))] +pub fn remove_shortcut() -> Result<()> { + Ok(()) +} + +#[cfg(not(target_os = "windows"))] +pub fn is_shortcut_enabled() -> Result { + Ok(false) +} diff --git a/src-tauri/src/utils/mod.rs b/src-tauri/src/utils/mod.rs index 2886bff3..7d3edcd0 100644 --- a/src-tauri/src/utils/mod.rs +++ b/src-tauri/src/utils/mod.rs @@ -1,3 +1,4 @@ +pub mod autostart; pub mod dirs; pub mod error; pub mod help;