Avoid UAC pop-up

This commit is contained in:
pompurin404 2024-08-24 17:40:01 +08:00
parent 34431dc7b7
commit fa9328b578
No known key found for this signature in database
8 changed files with 89 additions and 112 deletions

View File

@ -1,6 +1,7 @@
### New Features ### New Features
- 支持删除 Webdav 备份文件 - 支持删除 Webdav 备份文件
- Windows 绕过UAC弹窗
- Esc关闭窗口 - Esc关闭窗口
### Bug Fixes ### Bug Fixes

View File

@ -30,7 +30,6 @@ win:
- nsis - nsis
- 7z - 7z
artifactName: ${name}-windows-${version}-${arch}-portable.${ext} artifactName: ${name}-windows-${version}-${arch}-portable.${ext}
requestedExecutionLevel: highestAvailable
executableName: mihomo-party executableName: mihomo-party
nsis: nsis:
artifactName: ${name}-windows-${version}-${arch}-setup.${ext} artifactName: ${name}-windows-${version}-${arch}-setup.${ext}

View File

@ -11,14 +11,31 @@ import { createTray } from './resolve/tray'
import { init } from './utils/init' import { init } from './utils/init'
import { join } from 'path' import { join } from 'path'
import { initShortcut } from './resolve/shortcut' import { initShortcut } from './resolve/shortcut'
import { execSync } from 'child_process'
import { createElevateTask } from './sys/misc'
export let mainWindow: BrowserWindow | null = null export let mainWindow: BrowserWindow | null = null
if (process.platform === 'win32') {
try {
createElevateTask()
} catch (e) {
try {
execSync('schtasks /run /tn mihomo-party-run')
} catch (e) {
dialog.showErrorBox('首次启动请以管理员权限运行', '首次启动请以管理员权限运行')
} finally {
app.exit()
}
}
}
const gotTheLock = app.requestSingleInstanceLock() const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) { if (!gotTheLock) {
app.quit() app.quit()
} }
const initPromise = init() const initPromise = init()
app.on('second-instance', async (_event, commandline) => { app.on('second-instance', async (_event, commandline) => {

View File

@ -1,6 +1,6 @@
import { exec } from 'child_process'
import { dataDir, exePath, homeDir } from '../utils/dirs' import { dataDir, exePath, homeDir } from '../utils/dirs'
import { mkdir, readFile, rm, writeFile } from 'fs/promises' import { mkdir, readFile, rm, writeFile } from 'fs/promises'
import { exec } from 'child_process'
import { existsSync } from 'fs' import { existsSync } from 'fs'
import { app } from 'electron' import { app } from 'electron'
import { promisify } from 'util' import { promisify } from 'util'
@ -126,63 +126,3 @@ export async function disableAutoRun(): Promise<void> {
await rm(desktopFilePath) await rm(desktopFilePath)
} }
} }
const shortcutTaskXml = `<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>${new Date().toISOString()}</Date>
<Author>${process.env.USERNAME}</Author>
</RegistrationInfo>
<Triggers />
<Principals>
<Principal id="Author">
<LogonType>InteractiveToken</LogonType>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>Parallel</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<AllowHardTerminate>false</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>false</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT72H</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>${exePath()}</Command>
</Exec>
</Actions>
</Task>
`
export async function createShortcut(): Promise<void> {
if (process.platform === 'win32') {
const execPromise = promisify(exec)
const taskFilePath = path.join(dataDir(), `${appName}-run.xml`)
await writeFile(taskFilePath, Buffer.from(`\ufeff${shortcutTaskXml}`, 'utf-16le'))
await execPromise(`schtasks /create /tn "${appName}-run" /xml "${taskFilePath}" /f`)
const createShortcutCommand = `
$shortcutPath = [System.IO.Path]::Combine([System.Environment]::GetFolderPath("Desktop"), "Mihomo Party.lnk")
$wshell = New-Object -ComObject WScript.Shell
$shortcut = $wshell.CreateShortcut($shortcutPath)
$shortcut.TargetPath = "C:\\Windows\\System32\\schtasks.exe"
$shortcut.Arguments = "/run /tn ${appName}-run"
$shortcut.Description = "Start Mihomo Party without UAC Prompt"
$shortcut.IconLocation = "${exePath()}"
$shortcut.WindowStyle = 7
$shortcut.Save()`
await execPromise(createShortcutCommand, { shell: 'powershell' })
}
}

View File

@ -1,9 +1,11 @@
import { exec, execFile } from 'child_process' import { exec, execFile, execSync } from 'child_process'
import { dialog, nativeTheme } from 'electron' import { dialog, nativeTheme } from 'electron'
import { readFile } from 'fs/promises' import { readFile } from 'fs/promises'
import path from 'path' import path from 'path'
import os from 'os'
import { promisify } from 'util' import { promisify } from 'util'
import { exePath, mihomoCorePath, resourcesDir } from '../utils/dirs' import { exePath, mihomoCorePath, resourcesDir } from '../utils/dirs'
import { writeFileSync } from 'fs'
export function getFilePath(ext: string[]): string[] | undefined { export function getFilePath(ext: string[]): string[] | undefined {
return dialog.showOpenDialogSync({ return dialog.showOpenDialogSync({
@ -45,3 +47,49 @@ export async function setupFirewall(): Promise<void> {
export function setNativeTheme(theme: 'system' | 'light' | 'dark'): void { export function setNativeTheme(theme: 'system' | 'light' | 'dark'): void {
nativeTheme.themeSource = theme nativeTheme.themeSource = theme
} }
const elevateTaskXml = `<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>${new Date().toISOString()}</Date>
<Author>${process.env.USERNAME}</Author>
</RegistrationInfo>
<Triggers />
<Principals>
<Principal id="Author">
<LogonType>InteractiveToken</LogonType>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>Parallel</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<AllowHardTerminate>false</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>false</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT72H</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>${exePath()}</Command>
</Exec>
</Actions>
</Task>
`
export function createElevateTask(): void {
const taskFilePath = path.join(os.tmpdir(), `mihomo-party-run.xml`)
writeFileSync(taskFilePath, Buffer.from(`\ufeff${elevateTaskXml}`, 'utf-16le'))
execSync(`schtasks /create /tn "mihomo-party-run" /xml "${taskFilePath}" /f`)
}

View File

@ -21,7 +21,7 @@ import {
stopMihomoConnections, stopMihomoConnections,
stopMihomoLogs stopMihomoLogs
} from '../core/mihomoApi' } from '../core/mihomoApi'
import { checkAutoRun, createShortcut, disableAutoRun, enableAutoRun } from '../sys/autoRun' import { checkAutoRun, disableAutoRun, enableAutoRun } from '../sys/autoRun'
import { import {
getAppConfig, getAppConfig,
patchAppConfig, patchAppConfig,
@ -166,7 +166,6 @@ export function registerIpcMainHandlers(): void {
ipcMain.handle('registerShortcut', (_e, oldShortcut, newShortcut, action) => ipcMain.handle('registerShortcut', (_e, oldShortcut, newShortcut, action) =>
ipcErrorWrapper(registerShortcut)(oldShortcut, newShortcut, action) ipcErrorWrapper(registerShortcut)(oldShortcut, newShortcut, action)
) )
ipcMain.handle('createShortcut', ipcErrorWrapper(createShortcut))
ipcMain.handle('setNativeTheme', (_e, theme) => { ipcMain.handle('setNativeTheme', (_e, theme) => {
setNativeTheme(theme) setNativeTheme(theme)
}) })

View File

@ -1,4 +1,4 @@
import React, { Key, useState } from 'react' import React, { Key } from 'react'
import SettingCard from '../base/base-setting-card' import SettingCard from '../base/base-setting-card'
import SettingItem from '../base/base-setting-item' import SettingItem from '../base/base-setting-item'
import { Button, Select, SelectItem, Switch, Tab, Tabs } from '@nextui-org/react' import { Button, Select, SelectItem, Switch, Tab, Tabs } from '@nextui-org/react'
@ -7,7 +7,6 @@ import useSWR from 'swr'
import { import {
checkAutoRun, checkAutoRun,
copyEnv, copyEnv,
createShortcut,
disableAutoRun, disableAutoRun,
enableAutoRun, enableAutoRun,
isPortable, isPortable,
@ -23,7 +22,6 @@ const GeneralConfig: React.FC = () => {
const { data: enable, mutate: mutateEnable } = useSWR('checkAutoRun', checkAutoRun) const { data: enable, mutate: mutateEnable } = useSWR('checkAutoRun', checkAutoRun)
const { data: portable, mutate: mutatePortable } = useSWR('isPortable', isPortable) const { data: portable, mutate: mutatePortable } = useSWR('isPortable', isPortable)
const { appConfig, patchAppConfig } = useAppConfig() const { appConfig, patchAppConfig } = useAppConfig()
const [creating, setCreating] = useState(false)
const { setTheme } = useTheme() const { setTheme } = useTheme()
const { const {
silentStart = false, silentStart = false,
@ -166,26 +164,6 @@ const GeneralConfig: React.FC = () => {
</> </>
)} )}
{platform === 'win32' && ( {platform === 'win32' && (
<>
<SettingItem title="创建无UAC弹窗快捷方式" divider>
<Button
size="sm"
color="primary"
isLoading={creating}
onClick={async () => {
try {
setCreating(true)
await createShortcut()
} catch (e) {
alert(e)
} finally {
setCreating(false)
}
}}
>
</Button>
</SettingItem>
<SettingItem title="数据存储路径" divider> <SettingItem title="数据存储路径" divider>
<Select <Select
className="w-[150px]" className="w-[150px]"
@ -205,7 +183,6 @@ const GeneralConfig: React.FC = () => {
<SelectItem key="portable"></SelectItem> <SelectItem key="portable"></SelectItem>
</Select> </Select>
</SettingItem> </SettingItem>
</>
)} )}
<SettingItem title="背景色" divider={appTheme !== 'system'}> <SettingItem title="背景色" divider={appTheme !== 'system'}>
<Tabs <Tabs

View File

@ -291,10 +291,6 @@ export async function quitApp(): Promise<void> {
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('quitApp')) return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('quitApp'))
} }
export async function createShortcut(): Promise<void> {
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('createShortcut'))
}
export async function setNativeTheme(theme: 'system' | 'light' | 'dark'): Promise<void> { export async function setNativeTheme(theme: 'system' | 'light' | 'dark'): Promise<void> {
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setNativeTheme', theme)) return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setNativeTheme', theme))
} }