From 3c6ff7d20574cbdf0f83d6b41ddfa8aa87cdbdd5 Mon Sep 17 00:00:00 2001 From: lnkosadmin Date: Wed, 26 Aug 2026 18:48:17 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE | 21 +++++ README.md | 97 ++++++++++++++++++++ go.mod | 5 ++ go.sum | 2 + main.go | 261 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 386 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..635a32b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 editgpu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d62dd3a --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +# ⚠️ 重要声明 ⚠️ + +> # 🚨 免责声明 +> +> ## 本工具仅供学习交流使用! +> +> ### 请勿用于任何商业用途、违法用途或对他人造成损害的用途! +> +> **使用本工具所造成的一切后果,由使用者自行承担!** +> +> - 修改注册表可能导致系统不稳定、驱动异常、设备无法识别等问题 +> - 请在操作前自行备份注册表与重要数据 +> - 操作需谨慎,风险自负 + +--- + +# 显卡名称修改工具 (GPU Rename) + +一个用于修改 Windows 系统显卡显示名称的 Go 命令行小工具。 + +## ✨ 功能特性 + +- 自动枚举当前**实际连接**的物理显卡(PCI 总线),过滤掉幽灵设备与虚拟显示适配器 +- 显卡数量 > 1 时,列出列表供用户选择 +- 支持自定义显卡显示名称 +- 修改完成后自动验证结果 +- 修改成功后提示重启机器 + +## 🖥️ 运行环境 + +- Windows 7 / 8 / 10 / 11 +- 需要 **管理员权限** 运行(修改注册表 HKLM 需要) + +## 📦 编译 + +```bash +# 使用 Go 1.21+ 编译 +go mod tidy +go build -o editgpu.exe . +``` + +或直接使用已编译好的 `editgpu.exe`。 + +## 🚀 使用方法 + +1. **右键** 以管理员身份运行 `editgpu.exe` +2. 程序自动检测物理显卡: + + - 只有 1 块显卡:直接选中 + - 多块显卡:输入序号选择要修改的显卡 + +3. 输入新的显卡名称(直接回车可取消) +4. 程序自动修改注册表并验证 +5. 看到「修改成功」提示后,**重启电脑**使修改完全生效 + +## ⚙️ 工作原理 + +程序通过 Windows SetupAPI 枚举当前存在的显卡设备: + +1. 使用 `DIGCF_PRESENT` 标志只获取当前实际连接的设备,排除历史遗留的幽灵设备 +2. 通过 `SPDRP_ENUMERATOR_NAME == "PCI"` 过滤,只保留物理显卡,排除虚拟显示适配器 +3. 获取设备实例 ID,映射到注册表路径 `HKLM\SYSTEM\CurrentControlSet\Enum\...` +4. 修改该路径下的 `DeviceDesc` 注册表值 + +## 📁 文件结构 + +``` +editgpu/ +├── main.go # 主程序源码 +├── go.mod # Go 模块配置 +├── go.sum # 依赖校验文件 +└── README.md # 本说明文档 +``` + +## ❓ 常见问题 + +**Q:提示「请以管理员身份运行」?** +A:修改注册表需要管理员权限,请右键选择「以管理员身份运行」。 + +**Q:修改后设备管理器不显示新名称?** +A:请重启电脑,重启后设备管理器会刷新显示新名称。 + +**Q:修改名称会影响显卡性能吗?** +A:不会,仅修改显示名称,不影响硬件本身与驱动功能。 + +**Q:如何恢复原始名称?** +A:重新运行本工具,输入原始显卡名称再次修改即可;或到设备管理器卸载设备后重新扫描硬件。 + +--- + +# ⚠️ 再次提醒 ⚠️ + +> ## 本工具仅供学习交流使用 +> +> ### 因使用本工具产生的一切后果,由使用者自行承担! +> +> #### 请勿将本工具用于非法用途、商业用途或损害他人利益! diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..521882d --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module editgpu + +go 1.25.0 + +require golang.org/x/sys v0.47.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..37ee2d4 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= diff --git a/main.go b/main.go new file mode 100644 index 0000000..4198084 --- /dev/null +++ b/main.go @@ -0,0 +1,261 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strings" + + "golang.org/x/sys/windows" + "golang.org/x/sys/windows/registry" +) + +// 显卡类 GUID +var gpuClassGUID = windows.GUID{ + Data1: 0x4d36e968, + Data2: 0xe325, + Data3: 0x11ce, + Data4: [8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}, +} + +// GPUInfo 保存一张显卡的信息 +type GPUInfo struct { + Name string // 显示名称 + Path string // 注册表枚举路径(HKLM\SYSTEM\CurrentControlSet\Enum\...) + ID string // 设备实例 ID +} + +func main() { + fmt.Println("============================================") + fmt.Println(" 显卡名称修改工具 (GPU Rename) ") + fmt.Println("============================================") + fmt.Println() + + // 1. 枚举当前实际存在的物理显卡 + gpus, err := enumerateGPUs() + if err != nil { + fmt.Printf("错误:枚举显卡失败 - %v\n", err) + fmt.Println("提示:请以管理员身份运行本程序。") + waitEnter() + return + } + + if len(gpus) == 0 { + fmt.Println("错误:未检测到任何物理显卡设备。") + waitEnter() + return + } + + // 2. 选择目标显卡 + var target GPUInfo + if len(gpus) > 1 { + fmt.Printf("检测到 %d 块物理显卡,请选择要修改的显卡:\n", len(gpus)) + for i, g := range gpus { + fmt.Printf(" [%d] %s\n", i+1, g.Name) + } + fmt.Println() + target, err = chooseGPU(gpus) + if err != nil { + fmt.Printf("错误:%v\n", err) + waitEnter() + return + } + } else { + target = gpus[0] + fmt.Printf("检测到 1 块物理显卡:%s\n\n", target.Name) + } + + fmt.Printf("当前选中显卡:%s\n", target.Name) + fmt.Printf("设备实例 ID:%s\n\n", target.ID) + + // 3. 输入新名称 + newName := readNewName() + if newName == "" { + fmt.Println("已取消操作。") + waitEnter() + return + } + + fmt.Printf("即将把显卡名称修改为:%s\n", newName) + + // 4. 执行修改 + if err := renameGPU(target, newName); err != nil { + fmt.Printf("错误:修改失败 - %v\n", err) + fmt.Println("请确认已使用管理员权限运行。") + waitEnter() + return + } + + fmt.Println("修改已写入注册表,正在验证...") + + // 5. 验证修改结果 + verifyName, err := verifyGPU(target) + if err != nil { + fmt.Printf("警告:验证时发生错误 - %v\n", err) + } else if verifyName == newName { + fmt.Println("验证通过:显卡名称已成功修改为指定值。") + } else { + fmt.Printf("警告:验证结果与预期不一致,当前值为:%s\n", verifyName) + fmt.Println(" 部分设备可能需要重启后才会在设备管理器中刷新。") + } + + fmt.Println() + fmt.Println("============================================") + fmt.Println(" 修改成功!请重启机器以使修改完全生效。 ") + fmt.Println("============================================") + + waitEnter() +} + +// enumerateGPUs 使用 SetupAPI 枚举当前实际存在的物理显卡。 +// 使用 DIGCF_PRESENT 标志,只列出真正连接在机器上的设备, +// 并过滤掉虚拟显示适配器(无物理设备对象名)。 +func enumerateGPUs() ([]GPUInfo, error) { + // 只枚举当前存在的设备(DIGCF_PRESENT) + devSet, err := windows.SetupDiGetClassDevsEx( + &gpuClassGUID, + "", + 0, + windows.DIGCF_PRESENT, + 0, + "", + ) + if err != nil { + return nil, err + } + defer windows.SetupDiDestroyDeviceInfoList(devSet) + + var gpus []GPUInfo + for i := 0; ; i++ { + devInfo, err := windows.SetupDiEnumDeviceInfo(devSet, i) + if err != nil { + break // 枚举完毕 + } + + // 过滤:只保留物理显卡(PCI 总线上的设备), + // 排除 ROOT\SWD 等虚拟显示适配器。 + enumerator := "" + if v, err := windows.SetupDiGetDeviceRegistryProperty(devSet, devInfo, windows.SPDRP_ENUMERATOR_NAME); err == nil { + if s, ok := v.(string); ok { + enumerator = s + } + } + if !strings.EqualFold(enumerator, "PCI") { + continue + } + + // 获取实例 ID + instanceID, err := windows.SetupDiGetDeviceInstanceId(devSet, devInfo) + if err != nil { + continue + } + + // 获取显示名称(优先 FriendlyName,其次 DeviceDesc) + name := getDeviceName(devSet, devInfo, instanceID) + + gpus = append(gpus, GPUInfo{ + Name: name, + Path: instanceIDToRegPath(instanceID), + ID: instanceID, + }) + } + + return gpus, nil +} + +// getDeviceName 获取设备显示名称,优先 FriendlyName,回退到 DeviceDesc +func getDeviceName(devSet windows.DevInfo, devInfo *windows.DevInfoData, fallback string) string { + if v, err := windows.SetupDiGetDeviceRegistryProperty(devSet, devInfo, windows.SPDRP_FRIENDLYNAME); err == nil { + if s, ok := v.(string); ok && strings.TrimSpace(s) != "" { + return s + } + } + if v, err := windows.SetupDiGetDeviceRegistryProperty(devSet, devInfo, windows.SPDRP_DEVICEDESC); err == nil { + if s, ok := v.(string); ok && strings.TrimSpace(s) != "" { + return cleanDeviceDesc(s) + } + } + return fallback +} + +// instanceIDToRegPath 将设备实例 ID 转换为注册表枚举路径。 +// 例如 PCI\VEN_10DE&DEV_1DB1...\4&31024D2F&0&0018 +// -> SYSTEM\CurrentControlSet\Enum\PCI\VEN_10DE&DEV_1DB1...\4&31024D2F&0&0018 +func instanceIDToRegPath(instanceID string) string { + return `SYSTEM\CurrentControlSet\Enum\` + instanceID +} + +// cleanDeviceDesc 去掉 DeviceDesc 中的 INF 引用前缀,只保留可读名称 +func cleanDeviceDesc(desc string) string { + if idx := strings.LastIndex(desc, ";"); idx >= 0 && idx < len(desc)-1 { + return desc[idx+1:] + } + return desc +} + +// getDeviceDesc 读取注册表中的 DeviceDesc +func getDeviceDesc(regPath string) (string, error) { + k, err := registry.OpenKey(registry.LOCAL_MACHINE, regPath, registry.QUERY_VALUE) + if err != nil { + return "", err + } + defer k.Close() + v, _, err := k.GetStringValue("DeviceDesc") + return v, err +} + +// chooseGPU 让用户从列表中选择一块显卡 +func chooseGPU(gpus []GPUInfo) (GPUInfo, error) { + reader := bufio.NewReader(os.Stdin) + for { + fmt.Printf("请输入显卡序号 (1-%d):", len(gpus)) + input, _ := reader.ReadString('\n') + input = strings.TrimSpace(input) + + var idx int + if _, err := fmt.Sscanf(input, "%d", &idx); err != nil { + fmt.Println("输入无效,请输入数字。") + continue + } + if idx < 1 || idx > len(gpus) { + fmt.Printf("序号超出范围,请输入 1-%d。\n", len(gpus)) + continue + } + return gpus[idx-1], nil + } +} + +// readNewName 读取用户输入的新名称 +func readNewName() string { + reader := bufio.NewReader(os.Stdin) + fmt.Print("请输入新的显卡名称(直接回车取消):") + input, _ := reader.ReadString('\n') + return strings.TrimSpace(input) +} + +// renameGPU 修改注册表中的 DeviceDesc +func renameGPU(g GPUInfo, newName string) error { + k, err := registry.OpenKey(registry.LOCAL_MACHINE, g.Path, registry.SET_VALUE) + if err != nil { + return err + } + defer k.Close() + + return k.SetStringValue("DeviceDesc", newName) +} + +// verifyGPU 重新读取 DeviceDesc 进行验证 +func verifyGPU(g GPUInfo) (string, error) { + desc, err := getDeviceDesc(g.Path) + if err != nil { + return "", err + } + return cleanDeviceDesc(desc), nil +} + +// waitEnter 等待用户按键后退出 +func waitEnter() { + fmt.Println() + fmt.Print("按回车键退出...") + bufio.NewReader(os.Stdin).ReadString('\n') +}