mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2025-12-27 05:00:30 +08:00
Avoid UAC pop-up
This commit is contained in:
parent
34431dc7b7
commit
fa9328b578
@ -1,6 +1,7 @@
|
|||||||
### New Features
|
### New Features
|
||||||
|
|
||||||
- 支持删除 Webdav 备份文件
|
- 支持删除 Webdav 备份文件
|
||||||
|
- Windows 绕过UAC弹窗
|
||||||
- Esc关闭窗口
|
- Esc关闭窗口
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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) => {
|
||||||
|
|||||||
@ -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' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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`)
|
||||||
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -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,46 +164,25 @@ const GeneralConfig: React.FC = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{platform === 'win32' && (
|
{platform === 'win32' && (
|
||||||
<>
|
<SettingItem title="数据存储路径" divider>
|
||||||
<SettingItem title="创建无UAC弹窗快捷方式" divider>
|
<Select
|
||||||
<Button
|
className="w-[150px]"
|
||||||
size="sm"
|
size="sm"
|
||||||
color="primary"
|
selectedKeys={new Set([portable ? 'portable' : 'data'])}
|
||||||
isLoading={creating}
|
onSelectionChange={async (v) => {
|
||||||
onClick={async () => {
|
try {
|
||||||
try {
|
await setPortable(v.currentKey === 'portable')
|
||||||
setCreating(true)
|
} catch (e) {
|
||||||
await createShortcut()
|
alert(e)
|
||||||
} catch (e) {
|
} finally {
|
||||||
alert(e)
|
mutatePortable()
|
||||||
} finally {
|
}
|
||||||
setCreating(false)
|
}}
|
||||||
}
|
>
|
||||||
}}
|
<SelectItem key="data">AppData</SelectItem>
|
||||||
>
|
<SelectItem key="portable">安装目录</SelectItem>
|
||||||
创建快捷方式
|
</Select>
|
||||||
</Button>
|
</SettingItem>
|
||||||
</SettingItem>
|
|
||||||
<SettingItem title="数据存储路径" divider>
|
|
||||||
<Select
|
|
||||||
className="w-[150px]"
|
|
||||||
size="sm"
|
|
||||||
selectedKeys={new Set([portable ? 'portable' : 'data'])}
|
|
||||||
onSelectionChange={async (v) => {
|
|
||||||
try {
|
|
||||||
await setPortable(v.currentKey === 'portable')
|
|
||||||
} catch (e) {
|
|
||||||
alert(e)
|
|
||||||
} finally {
|
|
||||||
mutatePortable()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectItem key="data">AppData</SelectItem>
|
|
||||||
<SelectItem key="portable">安装目录</SelectItem>
|
|
||||||
</Select>
|
|
||||||
</SettingItem>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
<SettingItem title="背景色" divider={appTheme !== 'system'}>
|
<SettingItem title="背景色" divider={appTheme !== 'system'}>
|
||||||
<Tabs
|
<Tabs
|
||||||
|
|||||||
@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user