1
0
Files
nex/openspec/changes/fix-windows-desktop-packaging/design.md
lanyuanxiaoyao 64dc66afa6 fix: Windows 桌面应用打包问题修复
- 删除通用 desktop target,重命名 platform targets 为简短形式 (desktop-mac/win/linux)
- 构建产物文件名统一为 nex-{os}-{arch}[.exe] 格式
- Windows 托盘图标使用 .ico 格式(运行时按平台选择)
- Windows 原生对话框使用 user32.MessageBoxW 替代 msg * 命令
- 更新 README.md 和 package-macos.sh 中的引用
- 添加单元测试覆盖 MessageBoxW 封装和图标选择逻辑
- 同步更新 desktop-app spec 规范文档
2026-04-22 23:20:39 +08:00

3.8 KiB
Raw Blame History

Context

Nex 桌面应用是一个将后端服务Go/Gin与前端静态资源embed.FS打包为单一可执行文件的跨平台应用。当前 Windows 构建存在三类问题:

  1. 通用 desktop target 无平台感知:输出 build/nex.exe 后缀,且缺少 -H=windowsgui linker flag 导致控制台窗口闪现
  2. 系统托盘图标加载失败getlantern/systray 在 Windows 上期望 ICO 格式,代码传入了 64x64 的 PNG
  3. showError/showAbout 使用 msg *Windows Home 版本可能不可用,配合 -H=windowsgui 后行为不可预测,且不支持自定义标题栏

项目已有 assets/icon.ico256x256但代码未使用。

Goals / Non-Goals

Goals:

  • Windows 构建产物可直接双击运行(.exe 后缀、无控制台窗口)
  • 系统托盘图标在所有平台上正确加载
  • Windows 上使用原生 MessageBoxW 对话框替代 msg *
  • Makefile target 命名简洁统一

Non-Goals:

  • 不引入新的第三方依赖
  • 不改变 macOS/Linux 上的现有行为
  • 不涉及应用签名或代码公证(属于发布流程)
  • 不重构整体打包架构

Decisions

1. 删除通用 desktop target重命名平台 target

决策:删除 desktop targetdesktop-darwin/desktop-windows/desktop-linux 重命名为 desktop-mac/desktop-win/desktop-linux

理由:通用 target 在跨平台构建时必然需要条件判断,增加复杂度。按平台分离更明确,且项目已有先例。短命名 win/mac/linux 更简洁。

产物命名统一nex-{os}-{arch}[.exe]

  • nex-mac-arm64nex-mac-amd64
  • nex-win-amd64.exe
  • nex-linux-amd64

2. 托盘图标运行时按平台选择格式

决策:在 setupSystray 中根据 runtime.GOOS 选择图标文件:

  • Windows加载 assets/icon.ico256x256 ICO
  • 其他:加载 assets/icon.pngPNG

备选方案

  • Build tags + 编译时选择:增加文件数,维护成本高
  • 所有平台统一用 ICOLinux/macOS 的 systray 实现对 ICO 支持不一致

理由:运行时判断最简单,两个文件都已通过 embedfs.Assetsassets/*)嵌入,零额外成本。

3. Windows 原生对话框使用 user32.MessageBoxW

决策:通过 syscall 调用 user32.dllMessageBoxW,替换 msg *

实现方式

var (
    user32            = syscall.NewLazyDLL("user32.dll")
    procMessageBoxW   = user32.NewProc("MessageBoxW")
)
func messageBox(title, message string) {
    procMessageBoxW.Call(0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(message))),
        uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(title))), 0x10)
}

备选方案

  • 继续用 msg *:不解决 Home 版不可用、标题栏不支持的问题
  • rundll32 调用:同样不可靠
  • 引入 lxn/walk 等 GUI 库:引入重依赖,过度

理由MessageBoxW 是 Windows 原生 API所有版本都有-H=windowsgui 完美兼容,支持标题栏和图标类型,零依赖。使用 syscall(非 unsafe 外部依赖)即可。

4. showError/showAbout 统一用平台 switch

决策:保持现有的 switch runtime.GOOS 结构,仅替换 Windows 分支实现。macOSosascript和 Linuxzenity不变。

Risks / Trade-offs

  • [syscall 跨架构] MessageBoxWsyscall.NewLazyDLL 仅在 Windows 上有效 → 使用 runtime.GOOS 守卫,非 Windows 不会执行该路径,编译时通过 build 文件或运行时判断确保不触发
  • [ICO 嵌入体积] icon.ico 270KB已在 embedfs 中,不增加新体积 → 无风险
  • [Makefile 兼容性] 删除 desktop target 后CI/本地脚本如果引用它需更新 → 需检查是否有外部引用