🐛 Fixed the reboot issue on different platforms
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
// This file is automatically generated. DO NOT EDIT
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HotkeyService Windows全局热键服务
|
* HotkeyService Linux全局热键服务
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -53,14 +53,6 @@ export function RegisterHotkey(hotkey: models$0.HotkeyCombo | null): Promise<voi
|
|||||||
return $resultPromise;
|
return $resultPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* OnShutdown 关闭服务
|
|
||||||
*/
|
|
||||||
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(157291181) as any;
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UnregisterHotkey 取消注册全局热键
|
* UnregisterHotkey 取消注册全局热键
|
||||||
*/
|
*/
|
||||||
|
112
internal/services/restart_darwin.go
Normal file
112
internal/services/restart_darwin.go
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
//go:build darwin
|
||||||
|
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// restartApplication Darwin(macOS)平台的重启实现
|
||||||
|
func (s *SelfUpdateService) restartApplication() error {
|
||||||
|
// 获取当前可执行文件路径
|
||||||
|
exe, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get executable path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前工作目录
|
||||||
|
workDir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to get working directory", "error", err)
|
||||||
|
workDir = filepath.Dir(exe) // 如果获取失败,使用可执行文件所在目录
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在macOS上,我们使用一个shell脚本来重启应用程序
|
||||||
|
// 创建一个唯一的临时shell脚本
|
||||||
|
scriptPath := fmt.Sprintf("/tmp/restart_voidraft_%d_%d.sh", os.Getpid(), time.Now().Unix())
|
||||||
|
scriptContent := fmt.Sprintf(`#!/bin/bash
|
||||||
|
sleep 1
|
||||||
|
cd %s
|
||||||
|
%s %s &
|
||||||
|
rm "%s"
|
||||||
|
`,
|
||||||
|
shellEscape(workDir), shellEscape(exe),
|
||||||
|
shellEscapeArgs(os.Args[1:]), scriptPath)
|
||||||
|
|
||||||
|
s.logger.Info("Creating restart script", "path", scriptPath)
|
||||||
|
|
||||||
|
// 写入脚本文件
|
||||||
|
err = os.WriteFile(scriptPath, []byte(scriptContent), 0755)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create restart script: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动脚本
|
||||||
|
cmd := exec.Command("/bin/bash", scriptPath)
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
Setsid: true, // 创建新的会话,使进程独立于父进程
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to start restart script: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 给脚本一点时间启动
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
// 立即退出当前进程
|
||||||
|
os.Exit(0)
|
||||||
|
|
||||||
|
return nil // 不会执行到这里
|
||||||
|
}
|
||||||
|
|
||||||
|
// shellEscape 转义单个shell参数或路径
|
||||||
|
func shellEscape(arg string) string {
|
||||||
|
if arg == "" {
|
||||||
|
return "''"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果参数只包含安全字符,不需要转义
|
||||||
|
if isSafeShellString(arg) {
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用单引号转义,但需要处理参数中的单引号
|
||||||
|
// 将单引号替换为 '"'"'
|
||||||
|
escaped := strings.ReplaceAll(arg, "'", `'"'"'`)
|
||||||
|
return "'" + escaped + "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
// shellEscapeArgs 转义多个shell参数
|
||||||
|
func shellEscapeArgs(args []string) string {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var escaped []string
|
||||||
|
for _, arg := range args {
|
||||||
|
escaped = append(escaped, shellEscape(arg))
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(escaped, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// isSafeShellString 检查字符串是否包含需要转义的字符
|
||||||
|
func isSafeShellString(s string) bool {
|
||||||
|
// 只包含字母、数字、下划线、连字符、点号和斜杠的字符串是安全的
|
||||||
|
for _, r := range s {
|
||||||
|
if !((r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') ||
|
||||||
|
(r >= '0' && r <= '9') || r == '_' || r == '-' ||
|
||||||
|
r == '.' || r == '/') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(s) > 0
|
||||||
|
}
|
112
internal/services/restart_linux.go
Normal file
112
internal/services/restart_linux.go
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
//go:build linux
|
||||||
|
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// restartApplication Linux平台的重启实现
|
||||||
|
func (s *SelfUpdateService) restartApplication() error {
|
||||||
|
// 获取当前可执行文件路径
|
||||||
|
exe, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get executable path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前工作目录
|
||||||
|
workDir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to get working directory", "error", err)
|
||||||
|
workDir = filepath.Dir(exe) // 如果获取失败,使用可执行文件所在目录
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在Linux上,我们使用一个shell脚本来重启应用程序
|
||||||
|
// 创建一个唯一的临时shell脚本
|
||||||
|
scriptPath := fmt.Sprintf("/tmp/restart_voidraft_%d_%d.sh", os.Getpid(), time.Now().Unix())
|
||||||
|
scriptContent := fmt.Sprintf(`#!/bin/bash
|
||||||
|
sleep 1
|
||||||
|
cd %s
|
||||||
|
%s %s &
|
||||||
|
rm "%s"
|
||||||
|
`,
|
||||||
|
shellEscape(workDir), shellEscape(exe),
|
||||||
|
shellEscapeArgs(os.Args[1:]), scriptPath)
|
||||||
|
|
||||||
|
s.logger.Info("Creating restart script", "path", scriptPath)
|
||||||
|
|
||||||
|
// 写入脚本文件
|
||||||
|
err = os.WriteFile(scriptPath, []byte(scriptContent), 0755)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create restart script: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动脚本
|
||||||
|
cmd := exec.Command("/bin/bash", scriptPath)
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
Setsid: true, // 创建新的会话,使进程独立于父进程
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to start restart script: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 给脚本一点时间启动
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
// 立即退出当前进程
|
||||||
|
os.Exit(0)
|
||||||
|
|
||||||
|
return nil // 不会执行到这里
|
||||||
|
}
|
||||||
|
|
||||||
|
// shellEscape 转义单个shell参数或路径
|
||||||
|
func shellEscape(arg string) string {
|
||||||
|
if arg == "" {
|
||||||
|
return "''"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果参数只包含安全字符,不需要转义
|
||||||
|
if isSafeShellString(arg) {
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用单引号转义,但需要处理参数中的单引号
|
||||||
|
// 将单引号替换为 '"'"'
|
||||||
|
escaped := strings.ReplaceAll(arg, "'", `'"'"'`)
|
||||||
|
return "'" + escaped + "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
// shellEscapeArgs 转义多个shell参数
|
||||||
|
func shellEscapeArgs(args []string) string {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var escaped []string
|
||||||
|
for _, arg := range args {
|
||||||
|
escaped = append(escaped, shellEscape(arg))
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(escaped, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// isSafeShellString 检查字符串是否包含需要转义的字符
|
||||||
|
func isSafeShellString(s string) bool {
|
||||||
|
// 只包含字母、数字、下划线、连字符、点号和斜杠的字符串是安全的
|
||||||
|
for _, r := range s {
|
||||||
|
if !((r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') ||
|
||||||
|
(r >= '0' && r <= '9') || r == '_' || r == '-' ||
|
||||||
|
r == '.' || r == '/') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(s) > 0
|
||||||
|
}
|
118
internal/services/restart_windows.go
Normal file
118
internal/services/restart_windows.go
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// restartApplication Windows平台的重启实现
|
||||||
|
func (s *SelfUpdateService) restartApplication() error {
|
||||||
|
// 获取当前可执行文件路径
|
||||||
|
exe, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get executable path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前工作目录
|
||||||
|
workDir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to get working directory", "error", err)
|
||||||
|
workDir = filepath.Dir(exe) // 如果获取失败,使用可执行文件所在目录
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建唯一的批处理文件来重启应用程序
|
||||||
|
// 使用进程ID和时间戳确保文件名唯一性
|
||||||
|
batchFile := filepath.Join(os.TempDir(), fmt.Sprintf("restart_voidraft_%d_%d.bat", os.Getpid(), time.Now().Unix()))
|
||||||
|
|
||||||
|
// 正确转义命令行参数
|
||||||
|
escapedArgs := escapeWindowsArgs(os.Args[1:])
|
||||||
|
batchContent := fmt.Sprintf(`@echo off
|
||||||
|
timeout /t 1 /nobreak > NUL
|
||||||
|
cd /d "%s"
|
||||||
|
start "" "%s" %s
|
||||||
|
del "%s"
|
||||||
|
`, workDir, exe, escapedArgs, batchFile)
|
||||||
|
|
||||||
|
s.logger.Info("Creating batch file", "path", batchFile, "content", batchContent)
|
||||||
|
|
||||||
|
// 写入批处理文件
|
||||||
|
err = os.WriteFile(batchFile, []byte(batchContent), 0644)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create batch file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动批处理文件
|
||||||
|
cmd := exec.Command("cmd.exe", "/C", batchFile)
|
||||||
|
cmd.Stdout = nil
|
||||||
|
cmd.Stderr = nil
|
||||||
|
cmd.Stdin = nil
|
||||||
|
// 分离进程,这样即使父进程退出,批处理文件仍然会继续执行
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to start batch file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 立即退出当前进程
|
||||||
|
os.Exit(0)
|
||||||
|
|
||||||
|
return nil // 不会执行到这里
|
||||||
|
}
|
||||||
|
|
||||||
|
// escapeWindowsArgs 转义Windows命令行参数
|
||||||
|
func escapeWindowsArgs(args []string) string {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var escaped []string
|
||||||
|
for _, arg := range args {
|
||||||
|
escaped = append(escaped, escapeWindowsArg(arg))
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(escaped, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// escapeWindowsArg 转义单个Windows命令行参数
|
||||||
|
func escapeWindowsArg(arg string) string {
|
||||||
|
// 如果参数不包含空格、制表符、换行符、双引号或反斜杠,则不需要转义
|
||||||
|
if !strings.ContainsAny(arg, " \t\n\r\"\\") {
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
|
||||||
|
// 需要转义的参数用双引号包围
|
||||||
|
var result strings.Builder
|
||||||
|
result.WriteByte('"')
|
||||||
|
|
||||||
|
for i := 0; i < len(arg); i++ {
|
||||||
|
c := arg[i]
|
||||||
|
switch c {
|
||||||
|
case '"':
|
||||||
|
// 双引号需要转义
|
||||||
|
result.WriteString(`\"`)
|
||||||
|
case '\\':
|
||||||
|
// 反斜杠需要特殊处理
|
||||||
|
// 如果后面跟着双引号,需要转义反斜杠
|
||||||
|
if i+1 < len(arg) && arg[i+1] == '"' {
|
||||||
|
result.WriteString(`\\`)
|
||||||
|
} else {
|
||||||
|
result.WriteByte(c)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
result.WriteByte(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.WriteByte('"')
|
||||||
|
return result.String()
|
||||||
|
}
|
@@ -7,11 +7,7 @@ import (
|
|||||||
"github.com/creativeprojects/go-selfupdate"
|
"github.com/creativeprojects/go-selfupdate"
|
||||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
"voidraft/internal/models"
|
"voidraft/internal/models"
|
||||||
)
|
)
|
||||||
@@ -428,69 +424,7 @@ func (s *SelfUpdateService) getUpdateFromSource(ctx context.Context, sourceType
|
|||||||
|
|
||||||
// RestartApplication 重启应用程序
|
// RestartApplication 重启应用程序
|
||||||
func (s *SelfUpdateService) RestartApplication() error {
|
func (s *SelfUpdateService) RestartApplication() error {
|
||||||
|
return s.restartApplication()
|
||||||
// 获取当前可执行文件路径
|
|
||||||
exe, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get executable path: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Windows平台需要特殊处理
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
|
|
||||||
// 获取当前工作目录
|
|
||||||
workDir, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to get working directory", "error", err)
|
|
||||||
workDir = filepath.Dir(exe) // 如果获取失败,使用可执行文件所在目录
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建批处理文件来重启应用程序
|
|
||||||
// 批处理文件会等待当前进程退出,然后启动新进程
|
|
||||||
batchFile := filepath.Join(os.TempDir(), "restart_voidraft.bat")
|
|
||||||
batchContent := fmt.Sprintf(`@echo off
|
|
||||||
timeout /t 1 /nobreak > NUL
|
|
||||||
cd /d "%s"
|
|
||||||
start "" "%s" %s
|
|
||||||
del "%s"
|
|
||||||
`, workDir, exe, strings.Join(os.Args[1:], " "), batchFile)
|
|
||||||
|
|
||||||
s.logger.Info("Creating batch file", "path", batchFile, "content", batchContent)
|
|
||||||
|
|
||||||
// 写入批处理文件
|
|
||||||
err = os.WriteFile(batchFile, []byte(batchContent), 0644)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create batch file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 启动批处理文件
|
|
||||||
cmd := exec.Command("cmd.exe", "/C", batchFile)
|
|
||||||
cmd.Stdout = nil
|
|
||||||
cmd.Stderr = nil
|
|
||||||
cmd.Stdin = nil
|
|
||||||
// 分离进程,这样即使父进程退出,批处理文件仍然会继续执行
|
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
||||||
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cmd.Start()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to start batch file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 立即退出当前进程
|
|
||||||
os.Exit(0)
|
|
||||||
|
|
||||||
return nil // 不会执行到这里
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用syscall.Exec替换当前进程
|
|
||||||
err = syscall.Exec(exe, os.Args, os.Environ())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to exec: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateConfigVersion 更新配置中的版本号
|
// updateConfigVersion 更新配置中的版本号
|
||||||
|
Reference in New Issue
Block a user