[Golang] 纯文本查看 复制代码
package main
import (
"fmt"
"os/exec"
"path/filepath"
"time"
"github.com/kbinani/screenshot"
"github.com/lxn/walk"
. "github.com/lxn/walk/declarative"
"golang.org/x/sys/windows/registry"
)
var mw *walk.MainWindow
// var ni *walk.NotifyIcon
func main() {
var cloudAccounts *walk.ComboBox
var addButton, deleteButton, startButton, stopButton *walk.PushButton
MainWindow{
AssignTo: &mw,
Title: "账户管理器",
// Icon: "main.ico", // 设置主窗口图标
Layout: VBox{},
Children: []Widget{
GroupBox{
Title: "用户",
Layout: VBox{},
Children: []Widget{
ComboBox{
AssignTo: &cloudAccounts,
},
PushButton{
AssignTo: &addButton,
Text: "添加",
OnClicked: func() {
addAccount(cloudAccounts)
},
},
PushButton{
AssignTo: &deleteButton,
Text: "删除",
OnClicked: func() {
deleteAccount(cloudAccounts)
},
},
PushButton{
AssignTo: &startButton,
Text: "启动",
OnClicked: func() {
startGame(cloudAccounts)
},
},
PushButton{
AssignTo: &stopButton,
Text: "结束",
OnClicked: func() {
stopGame()
},
},
},
},
},
}.Create()
// 设置启动时的窗口大小
mw.SetSize(walk.Size{Width: 300, Height: 200})
// mw.SetX(700)
// mw.SetY(300)
//使用截图包获取屏幕尺寸
screenBounds := screenshot.GetDisplayBounds(0)
// 计算并设置窗口位置
bounds := mw.Bounds()
mw.SetX((screenBounds.Max.X ) / 2 - bounds.Width)
mw.SetY((screenBounds.Max.Y) / 2 - bounds.Height)
updateCloudAccounts(cloudAccounts)
mw.Run()
}
func addAccount(cloudAccounts *walk.ComboBox) {
// 创建一个对话框对象
dlg, err := walk.NewDialog(nil)
if err != nil {
walk.MsgBox(nil, "错误", "创建对话框失败: "+err.Error(), walk.MsgBoxIconError)
return
}
defer dlg.Dispose()
// 设置对话框属性
dlg.SetTitle("添加新账户")
// // 获取屏幕尺寸并计算居中位置
screenBounds := screenshot.GetDisplayBounds(0)
dlg.SetSize(walk.Size{Width: 300, Height: 150})
x := (screenBounds.Dx() - 400) / 2
y := (screenBounds.Dy() - 250) / 2
// 设置对话框位置
dlg.SetBounds(walk.Rectangle{
X: x,
Y: y,
Width: 300,
Height: 150,
})
dlg.SetLayout(walk.NewVBoxLayout())
// 使用这个简单的方法来居中显示对话框
dlg.SetOwner(nil)
// 创建一个文本标签控件
label, err := walk.NewLabel(dlg)
if err != nil {
walk.MsgBox(nil, "错误", "创建标签失败: "+err.Error(), walk.MsgBoxIconError)
return
}
label.SetText("请输入新账户名称:")
// 创建一个文本框控件
edit, err := walk.NewLineEdit(dlg)
if err != nil {
walk.MsgBox(nil, "错误", "创建输入框失败: "+err.Error(), walk.MsgBoxIconError)
return
}
// 创建一个按钮控件
button, err := walk.NewPushButton(dlg)
if err != nil {
walk.MsgBox(nil, "错误", "创建按钮失败: "+err.Error(), walk.MsgBoxIconError)
return
}
button.SetText("确定")
// 设置按钮点击事件
var newAccountName string
button.Clicked().Attach(func() {
if edit.Text() == "" {
walk.MsgBox(dlg, "错误", "账户名不能为空", walk.MsgBoxIconError)
return
}
newAccountName = edit.Text()
// 检查账户名是否已存在
existingAccounts := cloudAccounts.Model().([]string)
for _, account := range existingAccounts {
if account == newAccountName {
result := walk.MsgBox(dlg, "警告", "账户名已存在,是否覆盖?", walk.MsgBoxYesNo|walk.MsgBoxIconWarning)
if result == walk.DlgCmdNo {
return // 用户选择不覆盖,返回输入状态
}
break // 用户选择覆盖,继续执行
}
}
dlg.Accept()
})
// 显示对话框
if dlg.Run() != walk.DlgCmdOK {
return // 用户取消
}
// 源路径是当前用户的路径
srcPath := `Software\miHoYo\原神`
// 目标路径是用户列表中的新路径
destPath := `Software\miHoYo\米哈游启动器\原神\` + newAccountName
// 复制注册表项
err = copyRegistryKey(srcPath, destPath)
if err != nil {
walk.MsgBox(nil, "错误", "复制账户失败: "+err.Error(), walk.MsgBoxIconError)
return
}
// 更新云账户列表
updateCloudAccounts(cloudAccounts)
walk.MsgBox(nil, "成功", "账户已添加", walk.MsgBoxIconInformation)
}
//补充删除账户
func deleteAccount(cloudAccounts *walk.ComboBox) {
selectedAccount := cloudAccounts.Text()
if selectedAccount == "" {
walk.MsgBox(nil, "错误", "请先选择要删除的账户", walk.MsgBoxIconError)
return
}
result := walk.MsgBox(nil, "确认", "确定要删除账户 "+selectedAccount+" 吗?", walk.MsgBoxYesNo|walk.MsgBoxIconQuestion)
if result != walk.DlgCmdYes {
return
}
keyPath := `Software\miHoYo\米哈游启动器\原神\` + selectedAccount
err := registry.DeleteKey(registry.CURRENT_USER, keyPath)
if err != nil {
walk.MsgBox(nil, "错误", "删除账户失败: "+err.Error(), walk.MsgBoxIconError)
return
}
updateCloudAccounts(cloudAccounts)
walk.MsgBox(nil, "成功", "账户已删除", walk.MsgBoxIconInformation)
}
func updateCloudAccounts(cloudAccounts *walk.ComboBox) {
key, err := registry.OpenKey(registry.CURRENT_USER, `Software\miHoYo\米哈游启动器\原神`, registry.ENUMERATE_SUB_KEYS)
if err != nil {
walk.MsgBox(nil, "错误", "无法打开注册表键: "+err.Error(), walk.MsgBoxIconError)
return
}
defer key.Close()
subKeyNames, err := key.ReadSubKeyNames(-1)
if err != nil {
walk.MsgBox(nil, "错误", "无法读取子键: "+err.Error(), walk.MsgBoxIconError)
return
}
cloudAccounts.SetModel(subKeyNames)
}
func startGame(cloudAccounts *walk.ComboBox) {
selectedAccount := cloudAccounts.Text()
// 目标路径是当前用户的路径
destPath := `Software\miHoYo\原神`
// 源路径是用户列表中的新路径
srcPath := `Software\miHoYo\米哈游启动器\原神\` + selectedAccount
//需要对selectedAccount进行判断,如果为空,不进行复制,直接启动
if selectedAccount != "" {
err := copyRegistryKey(srcPath, destPath)
if err != nil {
walk.MsgBox(nil, "错误", "复制注册表失败: "+err.Error(), walk.MsgBoxIconError)
return
}
}
key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\原神`, registry.QUERY_VALUE)
if err != nil {
walk.MsgBox(nil, "错误", "无法打开注册表键: "+err.Error(), walk.MsgBoxIconError)
return
}
defer key.Close()
path, _, err := key.GetStringValue("InstallPath")
if err != nil {
walk.MsgBox(nil, "错误", "无法读取注册表值: "+err.Error(), walk.MsgBoxIconError)
return
}
gamePath := filepath.Join(path, "Genshin Impact Game", "YuanShen.exe")
// 显示游戏路径
// result := walk.MsgBox(nil, "游戏路径", "游戏路径是:\n"+gamePath+"\n\n是否继续?", walk.MsgBoxOKCancel|walk.MsgBoxIconInformation)
// if result != walk.DlgCmdOK {
// return // 用户选择取消,不继续执行
// }
// 先执行taskkill命令
killCmd := exec.Command("taskkill", "/F", "/IM", "YuanShen.exe")
killOutput, killErr := killCmd.CombinedOutput()
if killErr != nil {
fmt.Printf("关闭原神进程时出现错误(可能是进程不存在): %v\n输出: %s\n", killErr, string(killOutput))
// 不要因为taskkill失败就中断程序,因为可能是游戏本来就没有运行
}
// 等待一小段时间,确保进程完全关闭
time.Sleep(time.Second)
// 然后启动游戏
startCmd := exec.Command(gamePath)
startErr := startCmd.Start()
if startErr != nil {
errorMsg := fmt.Sprintf("无法启动游戏: %v", startErr)
fmt.Println(errorMsg)
walk.MsgBox(nil, "错误", errorMsg, walk.MsgBoxIconError)
} else {
// walk.MsgBox(nil, "成功", "游戏已成功启动", walk.MsgBoxIconInformation)
}
}
func stopGame() {
cmd := exec.Command("taskkill", "/F", "/IM", "YuanShen.exe")
output, err := cmd.CombinedOutput()
if err != nil {
if exitError, ok := err.(*exec.ExitError); ok {
// 进程不存在时,taskkill 会返回错误码 128
if exitError.ExitCode() == 128 {
walk.MsgBox(nil, "提示", "未找到正在运行的原神进程", walk.MsgBoxIconInformation)
} else {
errorMsg := fmt.Sprintf("结束进程时出错: %v\n输出: %s", err, string(output))
walk.MsgBox(nil, "错误", errorMsg, walk.MsgBoxIconError)
}
} else {
errorMsg := fmt.Sprintf("执行命令时出错: %v\n输出: %s", err, string(output))
walk.MsgBox(nil, "错误", errorMsg, walk.MsgBoxIconError)
}
} else {
// walk.MsgBox(nil, "成功", "原神进程已成功结束", walk.MsgBoxIconInformation)
}
}
func copyRegistryKey(srcPath, destPath string) error {
srcKey, err := registry.OpenKey(registry.CURRENT_USER, srcPath, registry.READ)
if err != nil {
return err
}
defer srcKey.Close()
destKey, _, err := registry.CreateKey(registry.CURRENT_USER, destPath, registry.ALL_ACCESS)
if err != nil {
return err
}
defer destKey.Close()
// 复制值
values, err := srcKey.ReadValueNames(-1)
if err != nil {
return err
}
for _, name := range values {
value, valType, err := srcKey.GetValue(name, nil)
if err != nil {
return err
}
switch valType {
case registry.SZ, registry.EXPAND_SZ:
if err := destKey.SetStringValue(name, string(value)); err != nil {
return err
}
case registry.BINARY:
binaryData, _, err := srcKey.GetBinaryValue(name)
if err != nil {
return err
}
if err := destKey.SetBinaryValue(name, binaryData); err != nil {
return err
}
case registry.DWORD:
dwordVal, _, err := srcKey.GetIntegerValue(name)
if err != nil {
return err
}
if err := destKey.SetDWordValue(name, uint32(dwordVal)); err != nil {
return err
}
case registry.QWORD:
qwordVal, _, err := srcKey.GetIntegerValue(name)
if err != nil {
return err
}
if err := destKey.SetQWordValue(name, qwordVal); err != nil {
return err
}
default:
walk.MsgBox(nil, "错误", "不支持的注册表值类型", walk.MsgBoxIconError)
return nil
}
}
// 复制键值
subKeys, err := srcKey.ReadSubKeyNames(-1)
if err != nil {
return err
}
for _, subKeyName := range subKeys {
err := copyRegistryKey(srcPath+"\\"+subKeyName, destPath+"\\"+subKeyName)
if err != nil {
return err
}
}
return nil
}