WinForms EXE 打包发布
概述
把已开发完成的 .NET Framework WinForms/WPF 桌面工程,交付为两种可直接分发的产物:
- 便携版 zip——解压即用,无需安装;
- 标准安装包 exe(Inno Setup,简体中文向导)——免管理员安装到用户目录,含桌面快捷方式与卸载入口。
核心原则:程序把数据库、日志、配置写在 exe 旁边,因此默认方案为 PrivilegesRequired=lowest 装入 %LocalAppData%\Programs(管理员方案需改 manifest,仅在用户明确要求时使用)。所有含中文的脚本/配置文件一律 UTF-8 with BOM。
流程总览
- 前置确认 → 2. 项目分析 → 3. 一键发布(推荐)或手工流程 → 4. 校验与交付提示。
Step 1 前置确认(信息不全时先问,不要臆测)
- 交付形式:便携版 / 安装包 / 都要(默认:都要)。
- 安装位置与权限:免管理员·用户目录(推荐)/ 管理员·Program Files。
- 打包信息:软件显示名(默认取主窗体
this.Text)、版本号(默认取 app.manifest 或 csprojVersion)、公司名。AppId:老项目升级必须复用既有 GUID;新项目由脚本自动生成一次并提醒保存。
Step 2 项目分析(只读,不改源码)
- 读主工程
.csproj:TargetFramework、OutputType=WinExe、PackageReference(有System.Data.SQLite.Core→ 输出含 x86/x64 原生驱动,必须随包)、CopyToOutputDirectory的随包内容文件(db/配置/模板)、ApplicationIcon。 - 读
app.manifest:版本号、requestedExecutionLevel、DPI 声明。 - 确认
bin/Release是否已有产物;没有也没关系,脚本会现场编译。
Step 3 一键发布(推荐路径)
写一个 pack.config.json(用 Write 工具,UTF-8;全部项目差异集中于此,避免中文参数经命令行传参导致乱码):
{
"solutionDir": "F:/WORK/<项目根目录>",
"mainCsproj": "04.His.UroLink/His.UroLink.csproj",
"appName": "尿液分析仪联机工作站",
"appNameEn": "htUroLink",
"appVersion": "1.25.7.9",
"publisher": "成都汇拓宇新科技有限公司",
"appId": "{F3A9C2E1-5D47-4B68-9C0A-2E4F6D8B1C35}",
"installerIcon": "04.His.UroLink/Resources/urolink.ico",
"mustExistFiles": ["his_urine.db", "sysconfig.hisx", "Resources/printurine.docx"],
"readmeExtras": "- his_urine.db 本地数据库(运行自动更新,请定期备份)\n- sysconfig.hisx 系统配置文件",
"dotNetRuntimeCheck": true,
"skipPortable": false,
"skipInstaller": false,
"skipSmokeTest": false
}
字段说明:mainCsproj 相对 solutionDir;mustExistFiles 是编译产物中必须存在的随包文件(缺失即报错终止);readmeExtras 追加到便携版说明的"目录说明"段(\n 换行);appId 留空则自动生成;首次出包后把生成的 GUID 回填进配置。mustExistFiles/installerIcon/appId 为可选;其余按需填写。
执行脚本:
powershell -ExecutionPolicy Bypass -File "<本技能目录>/scripts/publish_desktop_exe.ps1" -Config "<pack.config.json 的绝对路径>"
脚本自动完成:dotnet build -c Release → 校验输出(x86/x64 驱动 + mustExistFiles)→ robocopy 暂存便携目录(排除 *.pdb)+ 生成使用说明 → Compress-Archive 出 zip → 定位/安装 Inno Setup 6(winget,--source winget)→ 部署中文语言包(优先用技能内置 assets/ChineseSimplified.isl)→ 填充 assets/installer-template.iss 生成 .iss(UTF-8 BOM)→ ISCC.exe 编译出 setup exe → 静默冒烟测试(装→验→卸→验)→ 打印产物清单。
产物位于 <solutionDir>/Publish/:<NameEn>_portable_v<版本>.zip(便携版,文件名用 ASCII 避免编码问题)、installer/<NameEn>_Setup_v<版本>.exe、installer/<NameEn>.iss、portable/<NameEn>_<版本>/、pack.config.json(留在 Publish 下,下次升级改版本号后直接重跑脚本)。
Step 3b 手工流程(脚本不适用时)
旧式 packages.config 工程、msbuild/x86 平台、或需要特殊定制时,按 references/process-details.md 的九步手工执行(含每步完整命令与 .iss 关键指令速查表)。
Step 4 校验与交付
- 便携版抽查:zip 解压后目录结构完整(x86/x64、Resources、说明文件在位)。
- 安装包必须跑过冒烟测试;静默安装后 exe 不存在 = 测试机缺 .NET 4.8(属 [Code] 自检拦截,见排错手册)。
present_files呈现 setup exe + 便携 zip(+ .iss 供复用)。
收尾必提示(不可省略)
- 杀软误报:未签名安装包可能被 Defender/360 拦截;正式渠道建议代码签名,内网可加白名单。
- AppId 勿改:升级只改版本号重出包;换了 GUID 会被当成两个独立软件。
- 数据迁移:升级/卸载保留
his_urine.db、sysconfig.hisx即迁移历史数据;卸载器设计上不删用户数据。
排错
任何阶段报错,按症状查 references/troubleshooting.md(编译缺 targeting pack、winget msstore 源干扰、GitHub 被墙取语言包、iss 编码、SQLite Interop 缺失、装 Program Files 写不进数据库、DPI 模糊等)。
资源清单
scripts/publish_desktop_exe.ps1——一键发布流水线(PS 5.1 兼容,脚本体纯 ASCII)。assets/installer-template.iss——Inno Setup 模板(__TOKEN__占位符;含 .NET 4.8 自检 [Code] 段)。assets/portable-readme-template.txt——便携版使用说明模板。assets/ChineseSimplified.isl——官方简体中文语言包(内置,免联网)。references/process-details.md——手工全流程详解。references/troubleshooting.md——排错手册。