diff --git a/src-tauri/src/commands/system.rs b/src-tauri/src/commands/system.rs index fee45c4c..a5a857a1 100644 --- a/src-tauri/src/commands/system.rs +++ b/src-tauri/src/commands/system.rs @@ -184,7 +184,7 @@ pub fn check_process_running(program: &str) -> bool { let file_name = match resolved_path.file_name() { Some(name) => name.to_string_lossy().to_string(), None => { - log::warn!( + warn!( "check_process_running: cannot extract filename from '{}'", program ); @@ -664,29 +664,31 @@ pub fn has_quit_after_run_flag() -> bool { /// 自动迁移旧版注册表自启动到任务计划程序 #[cfg(windows)] pub fn migrate_legacy_autostart() { - if has_legacy_registry_autostart() { - if create_schtask_autostart().is_ok() { - remove_legacy_registry_autostart(); - } + if has_legacy_registry_autostart() && create_schtask_autostart(None).is_ok() { + remove_legacy_registry_autostart(); } // 兼容迁移:老版本已创建的计划任务可能缺少交互式运行或启动延迟,自动重建为新配置 if schtask_autostart_needs_refresh() { - if let Err(err) = create_schtask_autostart() { + if let Err(err) = create_schtask_autostart(None) { warn!("重建自启动计划任务失败: {}", err); } } } #[cfg(windows)] -fn create_schtask_autostart() -> Result<(), String> { +fn create_schtask_autostart(suffix: Option) -> Result<(), String> { use std::os::windows::process::CommandExt; let exe_path = std::env::current_exe().map_err(|e| format!("获取程序路径失败: {}", e))?; let exe = exe_path.to_string_lossy(); + let task_name = match suffix { + Some(name) => &format!("MXU-{}", name), + None => "MXU", + }; let output = std::process::Command::new("schtasks") .args([ "/create", "/tn", - "MXU", + task_name, "/tr", &format!("\"{}\" --autostart", exe), "/sc", @@ -790,10 +792,10 @@ fn has_legacy_registry_autostart() -> bool { /// 通过 Windows 任务计划程序启用开机自启动(以最高权限运行,避免 UAC 弹窗) #[tauri::command] -pub fn autostart_enable() -> Result<(), String> { +pub fn autostart_enable(suffix: Option) -> Result<(), String> { #[cfg(windows)] { - create_schtask_autostart()?; + create_schtask_autostart(suffix)?; remove_legacy_registry_autostart(); Ok(()) } @@ -805,14 +807,19 @@ pub fn autostart_enable() -> Result<(), String> { /// 通过 Windows 任务计划程序禁用开机自启动 #[tauri::command] -pub fn autostart_disable() -> Result<(), String> { +pub fn autostart_disable(suffix: Option) -> Result<(), String> { #[cfg(windows)] { use std::os::windows::process::CommandExt; + let task_name = match suffix { + Some(suffix) => &format!("MXU-{}", suffix), + None => "MXU", + }; let _ = std::process::Command::new("schtasks") - .args(["/delete", "/tn", "MXU", "/f"]) + .args(["/delete", "/tn", task_name, "/f"]) .creation_flags(CREATE_NO_WINDOW) - .output(); + .output() + .map_err(|e| format!("执行 schtasks 失败: {}", e))?; // 清理旧版注册表条目 remove_legacy_registry_autostart(); Ok(()) @@ -825,12 +832,16 @@ pub fn autostart_disable() -> Result<(), String> { /// 查询是否存在自启动(任务计划程序或旧版注册表) #[tauri::command] -pub fn autostart_is_enabled() -> bool { +pub fn autostart_is_enabled(suffix: Option) -> bool { #[cfg(windows)] { use std::os::windows::process::CommandExt; + let task_name = match suffix { + Some(suffix) => &format!("MXU-{}", suffix), + None => "MXU", + }; let schtask = std::process::Command::new("schtasks") - .args(["/query", "/tn", "MXU"]) + .args(["/query", "/tn", task_name]) .creation_flags(CREATE_NO_WINDOW) .output() .map(|o| o.status.success()) @@ -889,7 +900,7 @@ pub async fn get_web_server_port() -> u16 { return port; } for _ in 0..50 { - tokio::time::sleep(std::time::Duration::from_millis(100)).await; + tokio::time::sleep(Duration::from_millis(100)).await; let port = crate::web_server::get_actual_port(); if port != 0 { return port; diff --git a/src/components/settings/GeneralSection.tsx b/src/components/settings/GeneralSection.tsx index e29683c0..0ea27223 100644 --- a/src/components/settings/GeneralSection.tsx +++ b/src/components/settings/GeneralSection.tsx @@ -1,25 +1,25 @@ -import { useCallback, useEffect, useRef, useState } from 'react'; -import { useTranslation } from 'react-i18next'; +import {useCallback, useEffect, useRef, useState} from 'react'; +import {useTranslation} from 'react-i18next'; import { - Settings2, - ListChecks, - AppWindowMac, AlertCircle, + AppWindowMac, + BrushCleaning, + Check, + ChevronDown, + ListChecks, Maximize2, - Power, Play, + Power, Rocket, - ChevronDown, - Check, - BrushCleaning, + Settings2, } from 'lucide-react'; -import { useAppStore } from '@/stores/appStore'; -import { defaultAddTaskPanelHeight, defaultWindowSize } from '@/types/config'; -import { isTauri } from '@/utils/paths'; -import { SwitchButton } from '@/components/FormControls'; -import { DesktopOnlyWrapper } from '@/components/ui/DesktopOnlyWrapper'; -import { FrameRateSelector } from '../FrameRateSelector'; +import {useAppStore} from '@/stores/appStore'; +import {defaultAddTaskPanelHeight, defaultWindowSize} from '@/types/config'; +import {isTauri} from '@/utils/paths'; +import {SwitchButton} from '@/components/FormControls'; +import {DesktopOnlyWrapper} from '@/components/ui/DesktopOnlyWrapper'; +import {FrameRateSelector} from '../FrameRateSelector'; export function GeneralSection() { const { t } = useTranslation(); @@ -41,6 +41,7 @@ export function GeneralSection() { autoStartRemovedInstanceName, autoClearLogsOnLaunch, setAutoClearLogsOnLaunch, + projectInterface } = useAppStore(); // 开机自启动状态(直接从 Tauri 插件查询,不走 store) @@ -59,7 +60,7 @@ export function GeneralSection() { .then((os) => { isWindowsRef.current = os === 'windows'; if (isWindowsRef.current) { - tauriInvoke('autostart_is_enabled') + tauriInvoke('autostart_is_enabled', {suffix: projectInterface?.name}) .then(setAutoStartEnabled) .catch(() => {}); } else { @@ -92,7 +93,8 @@ export function GeneralSection() { try { if (isWindowsRef.current) { const { invoke } = await import('@tauri-apps/api/core'); - await invoke(enabled ? 'autostart_enable' : 'autostart_disable'); + await invoke(enabled ? 'autostart_enable' : 'autostart_disable', + {suffix: projectInterface?.name}); } else { const { enable, disable } = await import('@tauri-apps/plugin-autostart'); if (enabled) {