文章目录
- 使用Go语言实现智能EXE文件重命名工具 🛠️
- 引言
- 工具功能概述
- 核心技术实现
- Windows版本信息API调用
- 大模型API集成
- 交互式命令行界面
- 完整工作流程
- 实际应用示例
- 附录
- 完整代码
使用Go语言实现智能EXE文件重命名工具 🛠️
引言
在日常开发和软件管理中,我们经常会遇到需要整理大量EXE文件的情况。这些文件往往有着不规范的命名,如setup.exe、installer.exe等,难以直接了解其具体内容和版本。本文将介绍如何使用Go语言开发一个智能EXE文件重命名工具,它能自动提取文件的版本信息,并利用大模型API生成规范的命名建议。
工具功能概述
这个工具主要实现了以下功能:
- 📌 解析EXE文件的版本信息资源
- 🤖 利用大模型API生成智能命名建议
- 💻 提供友好的命令行交互界面
- 🔄 安全执行文件重命名操作
核心技术实现
Windows版本信息API调用
Windows系统通过version.dll提供了访问文件版本信息的API。我们的工具使用了三个关键函数:
- GetFileVersionInfoSizeW - 获取版本信息数据的大小
- GetFileVersionInfoW - 获取完整的版本信息数据
- VerQueryValueW - 查询特定的版本信息字段
在Go中调用这些API需要使用syscall包和unsafe指针操作:
var (versionDLL = syscall.NewLazyDLL("version.dll")procGetFileVersionInfoSizeW = versionDLL.NewProc("GetFileVersionInfoSizeW")procGetFileVersionInfoW = versionDLL.NewProc("GetFileVersionInfoW")procVerQueryValueW = versionDLL.NewProc("VerQueryValueW")
)
大模型API集成
工具集成了bigmodel API来生成智能命名建议(该模型免费,应付这种场景绰绰有余):
cfg := openai.DefaultConfig(apiKey)
cfg.BaseURL = "https://open.bigmodel.cn/api/paas/v4/"
client := openai.NewClientWithConfig(cfg)resp, err := client.CreateChatCompletion(context.Background(),openai.ChatCompletionRequest{Model: "GLM-4-Flash-250414",Messages: []openai.ChatCompletionMessage{{Role: openai.ChatMessageRoleUser,Content: prompt,},},MaxTokens: 100,Temperature: 0.3,},
)
我们设计了提示词(prompt),确保大模型返回规范的文件名:
- 包含产品名称和版本号
- 只使用字母、数字、中文、下划线、连字符
- 格式统一,如
产品名_版本号.exe或产品名-版本号.exe
交互式命令行界面
使用promptui库创建了友好的交互界面:
// 确认对话框
prompt := promptui.Select{Label: "是否根据建议重命名文件?",Items: []string{"是", "否"},
}// 文本输入框
prompt := promptui.Prompt{Label: "请输入新的文件名",Default: suggestedName,AllowEdit: true,
}
完整工作流程
实际应用示例

附录
完整代码
完整代码
package mainimport ("context""fmt""os""path/filepath""strings""syscall""unicode/utf16""unsafe""github.com/manifoldco/promptui"openai "github.com/sashabaranov/go-openai"
)var (versionDLL = syscall.NewLazyDLL("version.dll")procGetFileVersionInfoSizeW = versionDLL.NewProc("GetFileVersionInfoSizeW")procGetFileVersionInfoW = versionDLL.NewProc("GetFileVersionInfoW")procVerQueryValueW = versionDLL.NewProc("VerQueryValueW")
)// 检查BIG_MODEL_KEY环境变量
func checkBigModelKey() error {key := os.Getenv("BIG_MODEL_KEY")if key != "" {return nil // 已经设置了}fmt.Println("未找到BIG_MODEL_KEY环境变量,请手动设置。例如:set BIG_MODEL_KEY=你的API密钥")return fmt.Errorf("未设置BIG_MODEL_KEY环境变量")
}func utf16PtrFromString(s string) *uint16 {u := utf16.Encode([]rune(s + "\x00"))return &u[0]
}// Windows API方式获取版本信息
func GetFileVersionInfoAPI(path string) (map[string]string, error) {info := make(map[string]string)pPath := utf16PtrFromString(path)var handle uint32size, _, _ := procGetFileVersionInfoSizeW.Call(uintptr(unsafe.Pointer(pPath)),uintptr(unsafe.Pointer(&handle)),)if size == 0 {return nil, fmt.Errorf("GetFileVersionInfoSizeW failed")}buf := make([]byte, size)ret, _, _ := procGetFileVersionInfoW.Call(uintptr(unsafe.Pointer(pPath)),0,uintptr(size),uintptr(unsafe.Pointer(&buf[0])),)if ret == 0 {return nil, fmt.Errorf("GetFileVersionInfoW failed")}// 查询语言和代码页var transPtr uintptrvar transLen uint32subBlock := utf16PtrFromString(`\VarFileInfo\Translation`)ret, _, _ = procVerQueryValueW.Call(uintptr(unsafe.Pointer(&buf[0])),uintptr(unsafe.Pointer(subBlock)),uintptr(unsafe.Pointer(&transPtr)),uintptr(unsafe.Pointer(&transLen)),)if ret == 0 || transLen < 4 {return nil, fmt.Errorf("VerQueryValueW Translation failed")}lang := *(*uint16)(unsafe.Pointer(transPtr))codepage := *(*uint16)(unsafe.Pointer(transPtr + 2))langCode := fmt.Sprintf("%04x%04x", lang, codepage)fields := []string{"FileDescription","FileVersion","ProductName","ProductVersion","LegalCopyright","OriginalFilename","InternalName","CompanyName","Comments",}for _, field := range fields {block := fmt.Sprintf(`\StringFileInfo\%s\%s`, langCode, field)blockPtr := utf16PtrFromString(block)var valuePtr uintptrvar valueLen uint32ret, _, _ := procVerQueryValueW.Call(uintptr(unsafe.Pointer(&buf[0])),uintptr(unsafe.Pointer(blockPtr)),uintptr(unsafe.Pointer(&valuePtr)),uintptr(unsafe.Pointer(&valueLen)),)if ret != 0 && valueLen > 0 {val := syscall.UTF16ToString((*[1 << 16]uint16)(unsafe.Pointer(valuePtr))[:valueLen])info[field] = val}}return info, nil
}func getAPIKey() string {key := os.Getenv("BIG_MODEL_KEY")if key != "" {return key}fmt.Print("请输入百川大模型 API KEY(BIG_MODEL_KEY):")var input stringfmt.Scanln(&input)return strings.TrimSpace(input)
}func GetRenameSuggestion(info map[string]string, originalName string) (string, error) {apiKey := getAPIKey()if apiKey == "" {return "", fmt.Errorf("未提供 BIG_MODEL_KEY")}cfg := openai.DefaultConfig(apiKey)cfg.BaseURL = "https://open.bigmodel.cn/api/paas/v4/"client := openai.NewClientWithConfig(cfg)prompt := fmt.Sprintf(`请根据以下EXE文件的版本信息,给出一个简洁、规范的文件重命名建议。
原始文件名: %s版本信息:
- 文件描述: %s
- 文件版本: %s
- 产品名称: %s
- 产品版本: %s
- 版权信息: %s
- 原始文件名: %s
- 内部名称: %s
- 公司名称: %s
- 注释: %s重命名要求:
1. 使用中文或英文,简洁明了
2. 包含产品名称和版本号
3. 避免特殊字符,只使用字母、数字、中文、下划线、连字符
4. 格式建议: 产品名_版本号.exe 或 产品名-版本号.exe
5. 如果产品名包含特殊字符,请适当简化请只返回重命名后的文件名(包含.exe扩展名),不要其他解释。`,originalName,getValueOrDefault(info, "FileDescription"),getValueOrDefault(info, "FileVersion"),getValueOrDefault(info, "ProductName"),getValueOrDefault(info, "ProductVersion"),getValueOrDefault(info, "LegalCopyright"),getValueOrDefault(info, "OriginalFilename"),getValueOrDefault(info, "InternalName"),getValueOrDefault(info, "CompanyName"),getValueOrDefault(info, "Comments"))resp, err := client.CreateChatCompletion(context.Background(),openai.ChatCompletionRequest{Model: "GLM-4-Flash-250414",Messages: []openai.ChatCompletionMessage{{Role: openai.ChatMessageRoleUser,Content: prompt,},},MaxTokens: 100,Temperature: 0.3,},)if err != nil {return "", fmt.Errorf("API调用失败: %w", err)}if len(resp.Choices) == 0 {return "", fmt.Errorf("未返回有效响应")}suggestion := strings.TrimSpace(resp.Choices[0].Message.Content)return suggestion, nil
}func getValueOrDefault(info map[string]string, key string) string {if value, exists := info[key]; exists && value != "" {return value}return "(无)"
}// 确认是否重命名
func confirmRename() bool {prompt := promptui.Select{Label: "是否根据建议重命名文件?",Items: []string{"是", "否"},}_, result, err := prompt.Run()if err != nil {fmt.Printf("选择失败: %v\n", err)return false}return result == "是"
}// 输入新文件名
func inputNewFileName(suggestedName string) string {prompt := promptui.Prompt{Label: "请输入新的文件名",Default: suggestedName,AllowEdit: true,}result, err := prompt.Run()if err != nil {fmt.Printf("输入失败: %v\n", err)return ""}return result
}// 重命名文件
func renameFile(oldPath, newName string) error {dir := filepath.Dir(oldPath)newPath := filepath.Join(dir, newName)// 检查新文件名是否已存在if _, err := os.Stat(newPath); err == nil {return fmt.Errorf("文件 %s 已存在", newName)}// 执行重命名err := os.Rename(oldPath, newPath)if err != nil {return fmt.Errorf("重命名失败: %w", err)}fmt.Printf("文件已重命名为: %s\n", newName)return nil
}func main() {// 检查BIG_MODEL_KEYif err := checkBigModelKey(); err != nil {return}if len(os.Args) < 2 {fmt.Println("请指定EXE文件路径")fmt.Println("示例: .\\exeRename.exe your_file.exe")return}exePath := os.Args[1]info, err := GetFileVersionInfoAPI(exePath)if err != nil {fmt.Printf("错误: %v\n", err)return}// 显示版本信息fmt.Println("=== 版本信息 ===")fields := []string{"FileDescription", "FileVersion", "ProductName", "ProductVersion", "LegalCopyright", "OriginalFilename", "InternalName", "CompanyName", "Comments"}for _, field := range fields {value := info[field]if value == "" {value = "(无)"}fmt.Printf("%-20s: %s\n", field, value)}// 获取原始文件名(不含路径)originalName := exePathif lastSlash := strings.LastIndex(exePath, "\\"); lastSlash != -1 {originalName = exePath[lastSlash+1:]}if lastSlash := strings.LastIndex(originalName, "/"); lastSlash != -1 {originalName = originalName[lastSlash+1:]}// 获取重命名建议fmt.Println("\n=== 智能重命名建议 ===")suggestion, err := GetRenameSuggestion(info, originalName)if err != nil {fmt.Printf("获取重命名建议失败: %v\n", err)fmt.Println("请确保已设置环境变量 OPENAI_API_KEY")return}fmt.Printf("原始文件名: %s\n", originalName)fmt.Printf("建议重命名: %s\n", suggestion)// 询问是否重命名if confirmRename() {// 输入新文件名newName := inputNewFileName(suggestion)if newName != "" {// 执行重命名if err := renameFile(exePath, newName); err != nil {fmt.Printf("重命名失败: %v\n", err)}}}
}