diff --git a/changelog.md b/changelog.md index 85c9d92..f7f2da5 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,11 @@ +# 1.9.1 + +## 修复 (Fix) + +- 修复 Windows 下以管理员重启开启 TUN 时因单实例锁冲突导致的闪退问题 +- 修复托盘菜单开启 TUN 时管理员重启后继续执行导致的竞态问题 +- 修复关键资源文件复制失败时静默跳过导致内核启动异常的问题 + # 1.9.0 ## 新功能 (Feat) diff --git a/src/main/core/permissions.ts b/src/main/core/permissions.ts index c45b687..d14676a 100644 --- a/src/main/core/permissions.ts +++ b/src/main/core/permissions.ts @@ -282,7 +282,11 @@ export async function restartAsAdmin(forTun: boolean = true): Promise { managerLogger.info('Restarting as administrator with command', command) // 先启动 PowerShell(它会等待 1 秒),然后立即退出当前进程 - exec(command, { windowsHide: true }) + exec(command, { windowsHide: true }, (error) => { + if (error) { + managerLogger.error('Failed to start PowerShell for admin restart', error) + } + }) managerLogger.info('PowerShell command started, quitting app immediately') app.exit(0) } diff --git a/src/main/index.ts b/src/main/index.ts index 3edafd4..6397736 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -87,13 +87,13 @@ async function checkHighPrivilegeCoreEarly(): Promise { if (choice === 0) { try { await restartAsAdmin(false) - process.exit(0) + app.exit(0) } catch (error) { safeShowErrorBox('common.error.adminRequired', `${error}`) - process.exit(1) + app.exit(1) } } else { - process.exit(0) + app.exit(0) } } catch (e) { mainLogger.error('Failed to check high privilege core', e) @@ -151,12 +151,14 @@ app.whenReady().then(async () => { try { initCoreWatcher() - const [startPromise] = await startCore() - startPromise.then(async () => { - await initProfileUpdater() - await initWebdavBackupScheduler() - await checkAdminRestartForTun() - }) + const startPromises = await startCore() + if (startPromises.length > 0) { + startPromises[0].then(async () => { + await initProfileUpdater() + await initWebdavBackupScheduler() + await checkAdminRestartForTun() + }) + } } catch (e) { safeShowErrorBox('mihomo.error.coreStartFailed', `${e}`) } diff --git a/src/main/resolve/tray.ts b/src/main/resolve/tray.ts index ed16714..bf719d0 100644 --- a/src/main/resolve/tray.ts +++ b/src/main/resolve/tray.ts @@ -253,6 +253,9 @@ export const buildContextMenu = async (): Promise => { } } catch (error) { await trayLogger.warn('Permission check failed in tray', error) + item.checked = false + ipcMain.emit('updateTrayMenu') + return } await patchControledMihomoConfig({ tun: { enable }, dns: { enable: true } }) diff --git a/src/main/sys/sysproxy.ts b/src/main/sys/sysproxy.ts index 5733f73..18bea16 100644 --- a/src/main/sys/sysproxy.ts +++ b/src/main/sys/sysproxy.ts @@ -97,10 +97,15 @@ async function enableSysProxy(): Promise { } } else { // Windows / Linux 直接使用 sysproxy-rs - if (mode === 'auto') { - triggerAutoProxy(true, `http://${proxyHost}:${pacPort}/pac`) - } else { - triggerManualProxy(true, proxyHost, port, bypass.join(',')) + try { + if (mode === 'auto') { + triggerAutoProxy(true, `http://${proxyHost}:${pacPort}/pac`) + } else { + triggerManualProxy(true, proxyHost, port, bypass.join(',')) + } + } catch (error) { + await proxyLogger.error('Failed to enable system proxy', error) + throw error } } } @@ -114,8 +119,13 @@ async function disableSysProxy(): Promise { ) } else { // Windows / Linux 直接使用 sysproxy-rs - triggerAutoProxy(false, '') - triggerManualProxy(false, '', 0, '') + try { + triggerAutoProxy(false, '') + triggerManualProxy(false, '', 0, '') + } catch (error) { + await proxyLogger.error('Failed to disable system proxy', error) + throw error + } } } diff --git a/src/main/utils/init.ts b/src/main/utils/init.ts index 0b9eb52..0fd1810 100644 --- a/src/main/utils/init.ts +++ b/src/main/utils/init.ts @@ -189,14 +189,11 @@ async function initFiles(): Promise { await cp(sourcePath, targetPath, { recursive: true, force: true }) } catch (error: unknown) { const code = (error as NodeJS.ErrnoException).code - if (code === 'EPERM' || code === 'EBUSY') { - // 文件被占用,如果目标已存在则跳过 - if (existsSync(targetPath)) { - await initLogger.warn(`Skipping ${file}: file is in use`) - return - } + // 文件被占用或权限问题,如果目标已存在则跳过 + if ((code === 'EPERM' || code === 'EBUSY' || code === 'EACCES') && existsSync(targetPath)) { + await initLogger.warn(`Skipping ${file}: file is in use or permission denied`) + return } - // 其他错误或目标不存在时,向上抛出让 criticalFiles 检查处理 throw error } })