Files
voidraft/internal/services/restart_windows.go

119 lines
2.8 KiB
Go

//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()
}