2026-08-27 10:09:54 +08:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"golang.org/x/sys/windows"
|
|
|
|
|
"golang.org/x/sys/windows/registry"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var keyPath = `Software\Microsoft\Windows\CurrentVersion\Run`
|
|
|
|
|
|
2026-08-27 10:56:41 +08:00
|
|
|
// resolveExePath 优先用 os.Executable(),回退到 os.Args[0]
|
|
|
|
|
// 这能正确处理通过快捷方式 / PATH 启动时 os.Args[0] 不是绝对路径的情况
|
|
|
|
|
func resolveExePath() string {
|
|
|
|
|
if exe, err := os.Executable(); err == nil {
|
|
|
|
|
if abs, err := filepath.Abs(exe); err == nil {
|
|
|
|
|
return abs
|
|
|
|
|
}
|
|
|
|
|
return exe
|
|
|
|
|
}
|
|
|
|
|
abs, _ := filepath.Abs(os.Args[0])
|
|
|
|
|
return abs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 设置开机自启动
|
|
|
|
|
// exePath 传入会被忽略,内部一律以 os.Executable() 为准
|
2026-08-27 10:09:54 +08:00
|
|
|
func setAutoStart(appName string, exePath string) error {
|
2026-08-27 10:56:41 +08:00
|
|
|
key, _, err := registry.CreateKey(registry.CURRENT_USER, keyPath, registry.ALL_ACCESS) // 当前用户登录后启动
|
|
|
|
|
//key, _, err := registry.CreateKey(registry.LOCAL_MACHINE, keyPath, registry.ALL_ACCESS) // 登录前启动(需要管理员权限)
|
2026-08-27 10:09:54 +08:00
|
|
|
if err != nil {
|
|
|
|
|
if errors.Is(err, windows.ERROR_ACCESS_DENIED) {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("无法打开注册表键: %v", err)
|
|
|
|
|
}
|
|
|
|
|
defer key.Close()
|
|
|
|
|
|
2026-08-27 10:56:41 +08:00
|
|
|
// 使用真实的 exe 路径而不是 os.Args[0],避免相对路径
|
|
|
|
|
absPath := resolveExePath()
|
|
|
|
|
if absPath == "" {
|
|
|
|
|
return fmt.Errorf("无法确定可执行文件路径")
|
2026-08-27 10:09:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
args := getArgs()
|
|
|
|
|
|
2026-08-27 10:56:41 +08:00
|
|
|
// 路径包含空格时需要加引号
|
|
|
|
|
value := fmt.Sprintf(`"%s"`, absPath)
|
|
|
|
|
if args != "" {
|
|
|
|
|
value = value + " " + args
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = key.SetStringValue(appName, value)
|
2026-08-27 10:09:54 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("写入注册表失败: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-27 10:56:41 +08:00
|
|
|
// 移除开机自启动
|
2026-08-27 10:09:54 +08:00
|
|
|
func removeAutoStart(appName string) error {
|
|
|
|
|
key, err := registry.OpenKey(registry.CURRENT_USER, keyPath, registry.ALL_ACCESS)
|
2026-08-27 10:56:41 +08:00
|
|
|
//key, err := registry.OpenKey(registry.LOCAL_MACHINE, keyPath, registry.ALL_ACCESS)
|
2026-08-27 10:09:54 +08:00
|
|
|
if err != nil {
|
|
|
|
|
if errors.Is(err, windows.ERROR_ACCESS_DENIED) {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("无法打开注册表键: %v", err)
|
|
|
|
|
}
|
|
|
|
|
defer key.Close()
|
|
|
|
|
|
|
|
|
|
err = key.DeleteValue(appName)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("删除注册表项失败: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-27 10:56:41 +08:00
|
|
|
// 检查注册表启动项
|
2026-08-27 10:09:54 +08:00
|
|
|
func isInRegistry(appName string) (bool, error) {
|
2026-08-27 10:56:41 +08:00
|
|
|
// 检查当前用户注册表
|
2026-08-27 10:09:54 +08:00
|
|
|
userKey, err := registry.OpenKey(registry.CURRENT_USER, keyPath, registry.QUERY_VALUE)
|
|
|
|
|
if err == nil {
|
|
|
|
|
defer userKey.Close()
|
|
|
|
|
_, _, err := userKey.GetStringValue(appName)
|
|
|
|
|
if err == nil {
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-27 10:56:41 +08:00
|
|
|
// 检查本地机器注册表(需要管理员权限)
|
|
|
|
|
//systemKey, err := registry.OpenKey(registry.LOCAL_MACHINE, keyPath, registry.QUERY_VALUE)
|
|
|
|
|
//if err == nil {
|
|
|
|
|
// defer systemKey.Close()
|
|
|
|
|
// _, _, err := systemKey.GetStringValue(appName)
|
|
|
|
|
// if err == nil {
|
|
|
|
|
// return true, nil
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
|
2026-08-27 10:09:54 +08:00
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-27 10:56:41 +08:00
|
|
|
// 综合检查开机启动状态
|
2026-08-27 10:09:54 +08:00
|
|
|
func isAutoStartEnabled(appName string) bool {
|
2026-08-27 10:56:41 +08:00
|
|
|
// 检查注册表
|
|
|
|
|
if reg, _ := isInRegistry(appName); reg {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
2026-08-27 10:09:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getArgs() string {
|
2026-08-27 10:56:41 +08:00
|
|
|
// 构建参数字符串(兼容含空格的参数)
|
2026-08-27 10:09:54 +08:00
|
|
|
var argsBuilder strings.Builder
|
|
|
|
|
for _, arg := range os.Args[1:] {
|
|
|
|
|
if strings.ContainsAny(arg, " \t") {
|
|
|
|
|
argsBuilder.WriteString(fmt.Sprintf(`"%s" `, arg))
|
|
|
|
|
} else {
|
|
|
|
|
argsBuilder.WriteString(arg + " ")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return strings.TrimSpace(argsBuilder.String())
|
|
|
|
|
}
|