331 lines
17 KiB
Markdown
331 lines
17 KiB
Markdown
---
|
||
name: winforms-designer-package
|
||
description: 把编译好的 .NET Framework 4.8 WinForms 项目(exe + 依赖 DLL + 图标)用 Inno Setup 6 打包成可分发的单文件安装包(setup.exe)。当用户说"打包""安装包""setup""发布""做成安装程序""Inno Setup""生成 setup.exe""打安装包""做成 exe 安装包""封装发布""更新包",或要求把 F:\pycode\wpf\src\WinFormsDesigner.Fx48 下 Samples\COOO\scr\<项目名> 里编译好的 exe 打包成安装包,或对"成套报价软件"项目发版打包(含版本号递增/Gitea Release/更新包的完整 8 步发版链,见文末专章)时,必须使用本技能。即使用户没说"Inno Setup",只要是要把编译产物做成可分发/可安装的形式就触发。涵盖:识别要打包的文件、写 setup.iss 脚本、中文语言包安装、编译安装包、图标嵌入、Program Files 权限处理、卸载清理等完整发布流程。
|
||
---
|
||
|
||
# WinForms 设计器项目打包器
|
||
|
||
把 winforms-designer-compile 技能编译出的 `<项目名>.exe` + 依赖 DLL + 图标,用 **Inno Setup 6** 打包成一个**单文件 setup.exe** 安装包,带中文安装向导、开始菜单/桌面快捷方式、控制面板卸载项。
|
||
|
||
> 本技能流程在 GCS 铜排壳体计算项目中**完整实战验证过**,每个步骤都真实跑通。
|
||
|
||
## ⛔ 铁律:不做测试安装,直接交付正式版(用户强制,2026-09-24)
|
||
|
||
**禁止 AI 自己做任何安装测试**——包括但不限于:
|
||
- 编译"测试变体"安装包(降权限 PrivilegesRequired=lowest / 改临时目录)再静默安装验证
|
||
- 把 setup.exe 装到本机临时目录、虚拟机去验证启动/卸载
|
||
- 安装后自动运行程序验证功能
|
||
|
||
**正确做法**:按需求直接编译**正式版** setup.exe(含正确的默认安装目录、权限、快捷方式),**立即交付给用户**,由**用户本人**安装和测试。测试中发现的任何问题由用户反馈后在下轮修复。
|
||
|
||
**原因**(xuanxing 项目实测教训):AI 侧测试变体要在用户桌面弹 UAC/错误框(如降权限后写公共桌面快捷方式报 `IPersistFile::Save 失败 0x80070005 拒绝访问`)、留残留目录和卸载表项,还要反复等待静默安装——比用户直接装一遍正式版**更慢更吵**。打包环节唯一要"验证"的只有 ISCC 编译输出 `Successful compile` 和产物存在。
|
||
|
||
## 前提:这是编译之后的环节
|
||
|
||
本技能接在 winforms-designer-compile 之后。进本技能前,`scr\<项目名>\` 目录里应该已经有:
|
||
- `<项目名>.exe`(csc 编译产物)
|
||
- 第三方 `.dll`(如 `AdvancedDataGridView.dll`)
|
||
- 可选 `app.ico`(程序图标)
|
||
|
||
如果还没有 exe,先去用 winforms-designer-compile 技能编译。
|
||
|
||
## 触发前提
|
||
|
||
- Inno Setup 6 已安装(默认在 `C:\Program Files (x86)\Inno Setup 6\`)。没装的话提示用户先装。
|
||
- 项目目录约定在 `F:\pycode\wpf\src\WinFormsDesigner.Fx48\bin\Debug\net48\Samples\COOO\scr\<项目名>\`。
|
||
|
||
---
|
||
|
||
## 📋 完整打包流程(5 步)
|
||
|
||
### 第 1 步:识别需要打包的文件
|
||
|
||
进项目目录 `scr\<项目名>\`,按下表分类:
|
||
|
||
| 文件类型 | 来源 | 是否打包 |
|
||
|---------|------|---------|
|
||
| `<项目名>.exe` | csc 编译产物 | ✅ 必需 |
|
||
| 第三方 `.dll`(如 `AdvancedDataGridView.dll`) | 设计器/代码依赖 | ✅ 必需(缺了程序跑不起来) |
|
||
| `app.ico`(若有) | 自制图标 | ✅ 建议带(用于快捷方式) |
|
||
| `.cs` / `.ui.cs` / `.xml` 源码 | — | ❌ 不打包 |
|
||
| `build.bat` / `build.rsp` / `*.log` | — | ❌ 不打包 |
|
||
| `单价.cfg` / `*.cfg` 配置 | 运行时生成 | ⚠️ 一般不打包(程序用默认值重建) |
|
||
|
||
**判断要打包哪些 DLL**:读 `<窗体名>.dependencies.txt`(设计器生成的权威清单),或直接 `ls *.dll` 看项目里实际存在的 DLL。
|
||
|
||
### 第 2 步:建发布目录 + 复制运行时文件
|
||
|
||
在 `Samples\COOO\` 下建 `release\<项目名>\`,**只复制运行时必需文件**(源码和构建文件不进):
|
||
|
||
```bash
|
||
mkdir -p 'F:\pycode\wpf\src\WinFormsDesigner.Fx48\bin\Debug\net48\Samples\COOO\release\<项目名>'
|
||
cp 'F:\...\scr\<项目名>\<项目名>.exe' 'F:\...\release\<项目名>\/'
|
||
cp 'F:\...\scr\<项目名>\AdvancedDataGridView.dll' 'F:\...\release\<项目名>\/' # 按实际依赖
|
||
cp 'F:\...\scr\<项目名>\app.ico' 'F:\...\release\<项目名>\/' # 若有图标
|
||
```
|
||
|
||
### 第 3 步:检查 Inno Setup 环境 + 中文语言包
|
||
|
||
**检查 Inno Setup**:
|
||
|
||
```bash
|
||
ls 'C:\Program Files (x86)\Inno Setup 6\ISCC.exe' 2>&1 && echo 'Inno Setup 已安装' || echo '未安装,需先装 Inno Setup 6'
|
||
```
|
||
|
||
**中文语言包**:Inno Setup 6 **默认不带中文简体语言包**。要做中文界面必须下载 `ChineseSimplified.isl` 放到 `C:\Program Files (x86)\Inno Setup 6\Languages\`(需管理员权限)。下载用 kira-96 社区维护版(已验证可用):
|
||
|
||
```bash
|
||
# 1. 下载到可写位置
|
||
curl -sL -o ChineseSimplified.isl 'https://raw.githubusercontent.com/kira-96/Inno-Setup-Chinese-Simplified-Translation/main/ChineseSimplified.isl'
|
||
|
||
# 2. 需管理员权限复制到 Program Files
|
||
powershell -NoProfile -Command "Start-Process cmd -ArgumentList '/c copy /Y ChineseSimplified.isl \"C:\Program Files (x86)\Inno Setup 6\Languages\\\"' -Verb RunAs -Wait"
|
||
|
||
# 3. 验证
|
||
ls 'C:\Program Files (x86)\Inno Setup 6\Languages\ChineseSimplified.isl'
|
||
```
|
||
|
||
> ⚠️ **下载的语言包必须带 UTF-8 BOM**。下载后检查,没有 BOM 就补一个,否则中文界面会乱码:
|
||
> ```bash
|
||
> python -c "
|
||
> import os
|
||
> p=r'C:\Program Files (x86)\Inno Setup 6\Languages\ChineseSimplified.isl'
|
||
> d=open(p,'rb').read()
|
||
> if d[:3]!=b'\xef\xbb\xbf': open(p,'wb').write(b'\xef\xbb\xbf'+d); print('已补 BOM')
|
||
> "
|
||
> ```
|
||
> (Program Files 需要管理员权限写入,可能要提权 PowerShell 跑。)
|
||
|
||
### 第 4 步:编写 setup.iss 脚本
|
||
|
||
在 `release\<项目名>\` 目录下建 `setup.iss`。下面是**实战验证过的完整模板**(GCS 铜排壳体计算项目),按需替换 `<...>` 占位:
|
||
|
||
```iss
|
||
; ============================================================
|
||
; <项目名> 安装包脚本 — Inno Setup 6
|
||
; 文件必须保存为 UTF-8 with BOM 编码(中文才能正确显示)
|
||
; ============================================================
|
||
|
||
[Setup]
|
||
AppName=<项目显示名>
|
||
AppVersion=1.0
|
||
AppPublisher=<作者或公司>
|
||
DefaultDirName=C:\<项目目录名>
|
||
DefaultGroupName=<项目显示名>
|
||
DisableProgramGroupPage=yes
|
||
OutputDir=Output
|
||
OutputBaseFilename=<项目名>_Setup
|
||
Compression=lzma2
|
||
SolidCompression=yes
|
||
WizardStyle=modern
|
||
ArchitecturesAllowed=x64
|
||
ArchitecturesInstallIn64BitMode=x64
|
||
PrivilegesRequired=admin
|
||
|
||
; 安装包自身图标(用程序的 app.ico)
|
||
SetupIconFile=app.ico
|
||
UninstallDisplayIcon={app}\app.ico
|
||
UninstallDisplayName=<项目显示名>
|
||
|
||
[Languages]
|
||
Name: "chinesesimp"; MessagesFile: "compiler:Languages\ChineseSimplified.isl"
|
||
|
||
[Tasks]
|
||
Name: "desktopicon"; Description: "创建桌面快捷方式(&D)"; GroupDescription: "附加图标:"
|
||
|
||
[Files]
|
||
Source: "<项目名>.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||
Source: "<第三方DLL>.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||
Source: "app.ico"; DestDir: "{app}"; Flags: ignoreversion
|
||
|
||
[Icons]
|
||
; 开始菜单快捷方式
|
||
Name: "{group}\<项目显示名>"; Filename: "{app}\<项目名>.exe"; IconFilename: "{app}\app.ico"; Comment: "<项目描述>"
|
||
Name: "{group}\卸载 <项目显示名>"; Filename: "{uninstallexe}"
|
||
; 桌面快捷方式
|
||
Name: "{commondesktop}\<项目显示名>"; Filename: "{app}\<项目名>.exe"; IconFilename: "{app}\app.ico"; Tasks: desktopicon
|
||
|
||
[Run]
|
||
; 安装完成可选启动
|
||
Filename: "{app}\<项目名>.exe"; Description: "立即启动 <项目显示名>"; Flags: nowait postinstall skipifsilent
|
||
|
||
[UninstallDelete]
|
||
; 卸载时彻底清理 {app} 目录
|
||
Type: filesandordirs; Name: "{app}"
|
||
```
|
||
|
||
#### setup.iss 段语法说明
|
||
|
||
| 段 | 作用 |
|
||
|----|------|
|
||
| `[Setup]` | 安装包基本信息、压缩、平台、权限。`DefaultDirName` = 默认安装位置(`{pf}` = Program Files,或直接写 `C:\xxx`)|
|
||
| `[Languages]` | 界面语言。中文用 `chinesesimp` + `ChineseSimplified.isl` |
|
||
| `[Tasks]` | 可勾选任务。桌面快捷方式任务默认勾选(**不要加 `Flags: checked`**,新版不认)|
|
||
| `[Files]` | 要打包的文件。`Flags: ignoreversion` = 不比对版本直接覆盖 |
|
||
| `[Icons]` | 快捷方式。`{group}` = 开始菜单文件夹,`{app}` = 安装目录,`{commondesktop}` = 公共桌面 |
|
||
| `[Run]` | 安装后动作。`postinstall` = 勾选"立即启动",`skipifsilent` = 静默安装时不启动 |
|
||
| `[UninstallDelete]` | 卸载时额外删除。`filesandordirs` 删整个目录及内容 |
|
||
|
||
#### ⚠️ setup.iss 的两个必做细节
|
||
|
||
**细节 1:必须 UTF-8 with BOM 编码**。中文(AppName、说明等)才能正确显示,否则装界面是乱码。写完 .iss 后检查并补 BOM:
|
||
|
||
```bash
|
||
python -c "
|
||
data = open('setup.iss','rb').read()
|
||
if data[:3] != b'\xef\xbb\xbf':
|
||
open('setup.iss','wb').write(b'\xef\xbb\xbf' + data)
|
||
print('已加 UTF-8 BOM')
|
||
"
|
||
```
|
||
|
||
**细节 2:`[Tasks]` 桌面快捷方式不要写 `Flags: checked`**。新版 Inno Setup 不认这个 flag,编译报 "Parameter Flags includes an unknown flag"。任务默认就是勾选的,直接省略 Flags。
|
||
|
||
### 第 5 步:编译安装包
|
||
|
||
```bash
|
||
cd 'F:\pycode\wpf\src\WinFormsDesigner.Fx48\bin\Debug\net48\Samples\COOO\release\<项目名>'
|
||
'C:\Program Files (x86)\Inno Setup 6\ISCC.exe' setup.iss 2>&1 | tail -5
|
||
```
|
||
|
||
**成功标志**:末尾输出 `Successful compile`,并在 `Output\<项目名>_Setup.exe` 生成安装包(通常 1~3 MB,LZMA2 压缩后远小于源文件总和)。
|
||
|
||
> 编译时 `Warning: A message named "..." has not been defined for "chinesesimp"` 是**正常的**——中文语言包版本与 Inno Setup 主程序版本不完全同步,缺失的消息自动用英文 Default.isl 回退,不影响功能。
|
||
|
||
---
|
||
|
||
## 发布目录最终结构
|
||
|
||
```
|
||
Samples\COOO\release\<项目名>\
|
||
├── <项目名>.exe ← 编译产物
|
||
├── <第三方DLL>.dll ← 依赖
|
||
├── app.ico ← 图标
|
||
├── setup.iss ← Inno Setup 脚本(源文件,留着方便改版重打包)
|
||
└── Output\
|
||
└── <项目名>_Setup.exe ← 最终交付的安装包
|
||
```
|
||
|
||
**交付物就是 `Output\<项目名>_Setup.exe` 这一个文件**,用户双击即可安装,自带中文向导、快捷方式、卸载项。
|
||
|
||
---
|
||
|
||
## 图标制作(可选,让 exe 有专属图标)
|
||
|
||
如果想给程序做专属图标嵌入 exe,详见 `references/icon-generation.md`(Pillow 纯 Python 绘制多尺寸 ICO 的完整模板,零原生依赖)。
|
||
|
||
简要流程:
|
||
1. 用 Pillow 绘制 512×512 图标 → 存成多尺寸 `app.ico`
|
||
2. 编译时在 `build.rsp` 加 `/win32icon:app.ico`
|
||
3. 窗体构造函数加 `LoadAppIcon()`(`Icon.ExtractAssociatedIcon`),让窗口/任务栏也显示图标
|
||
|
||
> 图标嵌入 exe 是**编译环节**做的事(/win32icon 是 csc 参数),打包环节只是把已有的 app.ico 一起带进安装包用于快捷方式。两环节配合。
|
||
|
||
---
|
||
|
||
## 常见问题排查
|
||
|
||
| 现象 | 原因 | 解决 |
|
||
|------|------|------|
|
||
| 安装界面中文乱码 | setup.iss 不是 UTF-8 BOM 编码 | 按第 4 步"细节 1"补 BOM 后重新编译 |
|
||
| `Couldn't open include file "...ChineseSimplified.isl"` | 没装中文语言包 | 按第 3 步下载 ChineseSimplified.isl 到 Inno Setup 的 Languages 目录 |
|
||
| 中文语言包装了还乱码 | 语言包 .isl 没 UTF-8 BOM | 给 .isl 文件补 BOM(需管理员权限)|
|
||
| `Parameter "Flags" includes an unknown flag` | `[Tasks]` 写了 `Flags: checked` | 删掉 `Flags: checked`,任务默认就勾选 |
|
||
| 安装到 Program Files 后程序保存配置报错 | exe 目录无写入权限 | 程序里配置文件改存 `%APPDATA%\<项目名>\`(用 `Environment.SpecialFolder.ApplicationData`),并在 `[UninstallDelete]` 加 `{userappdata}\<项目名>` 卸载时清理 |
|
||
| 装的 exe 双击没反应 | 缺少第三方 DLL | 检查 `[Files]` 是否把所有依赖 DLL 都打包了 |
|
||
| 卸载后配置文件残留 | `[UninstallDelete]` 没覆盖配置目录 | 加 `Type: filesandordirs; Name: "{userappdata}\<项目名>"` |
|
||
| `error CS0016: 未能写入输出文件`(重新编译 exe 时) | exe 被安装包运行的进程占用 | 先关掉运行中的程序再重新编译 |
|
||
|
||
---
|
||
|
||
## 发布前的完整流程(汇总)
|
||
|
||
```
|
||
1. [编译] 改完 .cs → 用 winforms-designer-compile 编译出 exe
|
||
2. [识别] 读 dependencies.txt 确定依赖 DLL
|
||
3. [建目录] 建 release\<项目名>\,复制 exe + DLL + ico
|
||
4. [写脚本] 写 setup.iss(用模板,改占位)+ 确保有 UTF-8 BOM
|
||
5. [语言包] 确认装了中文语言包(没有就下载,记得带 BOM)
|
||
6. [编译包] ISCC.exe setup.iss → 生成 Output\<项目名>_Setup.exe
|
||
7. [交付] 不做任何测试安装(见顶部铁律)——把 Output\<项目名>_Setup.exe 直接交给用户,由用户自行安装测试
|
||
```
|
||
|
||
---
|
||
|
||
## 🏭 项目专章:成套报价软件 完整发版体系(2026-10-01 定稿,用户强制)
|
||
|
||
> 仓库根:`C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件`。
|
||
> 该项目发版**不走上面的通用 5 步**,走下面的 **8 步固定链**(多了版本号递增、更新包、git、Gitea Release 环节)。
|
||
> 用户说"打包/发版/上传到 git"针对本项目时,必须按本节执行,顺序不可换。
|
||
|
||
```
|
||
① 递增版本号 → ② 清进程+编译 → ③ make_package 组装 → ④ ISCC 安装包
|
||
→ ⑤ make_update_package 更新包 → ⑥ git 提交推送 → ⑦ Gitea Release → ⑧ 验收
|
||
```
|
||
|
||
### ① 递增版本号(版本号铁律:每次发版必须递增)
|
||
|
||
```powershell
|
||
powershell -File tools\递增版本号.ps1 # 修订号+1;大版本: tools\递增版本号.ps1 1.1.0
|
||
```
|
||
唯一事实源 = `成套报价软件\Properties\AssemblyInfo.cs` 的 AssemblyVersion(脚本同步 setup.iss 的 AppVersion 与安装包文件名)。关于软件页运行时自动读程序集版本。输出必须显示两处 `[OK]`。
|
||
|
||
### ② 清进程 + 编译
|
||
|
||
```powershell
|
||
Get-Process | Where-Object { $_.Path -like '*成套报价软件*' -or $_.Path -like '*SharpDevelop Projects*' } | Stop-Process -Force
|
||
cd 成套报价软件 # ★ rsp 相对路径基于此子目录,在仓库根跑必报 CS2001
|
||
& "C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe" "@..\tools\verify_build.rsp" /warn:1
|
||
```
|
||
ExitCode=0 且无 warning 才继续(rsp 默认 /warn:0 压警告,排查必须 /warn:1)。
|
||
|
||
### ③ 组装发布目录
|
||
|
||
```powershell
|
||
python tools\make_package.py # 仓库根跑
|
||
```
|
||
核对输出:`DLL 复制完成,缺失: 无`;cadbt 段只有 node+CAD-Viewer(独立扒图 exe 已于 2026-10-01 退役);无 `!!` 警告。
|
||
|
||
### ④ ISCC 安装包
|
||
|
||
```powershell
|
||
& "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" "release\成套报价软件\setup.iss"
|
||
```
|
||
lzma2 约 2 分钟;产物 `release\成套报价软件\Output\成套报价软件_Setup_<版本>.exe`(约 120MB);`Successful compile` 才算成。中文语言包已装(chinesesimp 消息缺失 Warning 属正常)。
|
||
|
||
### ⑤ 更新包
|
||
|
||
先改 `tools\make_update_package.py` 两处:`PKG` 目录名带版本(如 `更新包_1.0.4`)+ `说明` 本期文案(只写用户可感知变化;使用方法写清覆盖哪个文件、怎么验证版本)。再跑 `python tools\make_update_package.py`。当前只含 成套报价软件.exe+使用说明(<1MB)。
|
||
|
||
### ⑥ git 提交推送
|
||
|
||
`git add -A` → `git commit -m "发版 v<版本>:..."` → `git push`(remote 已内嵌令牌)。
|
||
|
||
### ⑦ Gitea Release
|
||
|
||
tag=`v<版本>`(与 AssemblyInfo 一致)。PowerShell API 模式(令牌与仓库地址见 sd-enhanced-components 技能第 8 条或项目记忆):
|
||
- 建 Release:`Invoke-RestMethod -Method Post -Headers @{Authorization="token <令牌>"} -ContentType 'application/json; charset=utf-8' -Body ([Text.Encoding]::UTF8.GetBytes($body))`——**中文 body 必须 UTF8.GetBytes 否则乱码**
|
||
- 传附件:`System.Net.Http.HttpClient` + `MultipartFormDataContent`(字段名 `attachment`,PS5.1 无 -Form)
|
||
- Release 说明与更新包说明同源
|
||
|
||
### ⑧ 验收
|
||
|
||
附件大小合理(Setup ~120MB、更新包 <1MB);提醒用户装机验证:**关于软件页显示 v<版本>**、CAD/PDF 扒图窗口可用。
|
||
|
||
### 故障速查(本项目专属)
|
||
|
||
| 现象 | 处置 |
|
||
|------|------|
|
||
| 组装/替换文件报"正在使用" | ② 的进程没杀干净(查 node/AI 引擎/主程序) |
|
||
| 编译报 CS2001 全部源文件找不到 | 工作目录错了——必须 `成套报价软件\` 子目录 |
|
||
| ISCC 报文件找不到 | make_package 没跑或发布目录被清,回到 ③ |
|
||
| Release 附件上传 401 | 令牌失效——请用户在 Gitea 设置→应用重新生成(git remote 和 API 同步换) |
|
||
| push 被拒 fetch first | 用户网页传过文件——`git pull --rebase` 再 push,绝不 force push |
|
||
|
||
---
|
||
|
||
## 配套技能
|
||
|
||
- **winforms-designer-compile**:本技能的上游。先用它把 .cs + .ui.cs 编译成 exe,再交给本技能打包。
|
||
- **winforms-designer-xml**:生成/校验窗体 XML 布局,是整个流水线(XML → 编译 → 打包)的起点。
|