<p>Visual Studio 2022 不支持 Setup Project 部署 .NET 6+ WinForm 应用,推荐改用 WiX Toolset + WixSharp:先发布 self-contained 程序,再用 C# 定义 MSI 打包逻辑,注意设置 InstallScope.perMachine、嵌入 vc_redist.x64.exe 并通过日志调试。</p>

用 Visual Studio 自带的 Setup Project 生成安装包,现在行不通了
Visual Studio 2022 默认不带 Setup Project 模板,装了“Microsoft Visual Studio Installer Projects”扩展后,也只支持到 .NET Framework WinForm 项目。如果你用的是 .NET 6 或更高版本(哪怕只是选了 Windows Forms App (.NET) 模板),直接新建 Setup Project 会失败,或者安装时提示“无法加载程序集”“找不到 System.Windows.Forms”。这是因为新版 Setup Project 缺乏对单文件发布、运行时依赖和跨平台目标框架的感知。
推荐方案:用 WiX Toolset + WixSharp 替代
WiX 是微软官方维护的开源 Windows 安装包构建工具链,WixSharp 是它的 C# 封装,让你用写 C# 的方式定义安装逻辑,比纯 XML(.wxs)直观得多,且天然兼容 .NET 6+ 和 Self-contained 发布模式。
实操建议:
- 用
dotnet publish -c Release -r win-x64 --self-contained true先发布你的 WinForm 程序,得到完整可执行目录 - 新建一个
Console App (.NET)项目,NuGet 安装WixSharp - 在
Program.cs中写部署逻辑,核心是把发布目录打包进MsiPackage - 调用
project.BuildMsi()生成.msi文件,不是.exe
示例关键代码段:
var project = new Project("MyApp",
new Dir(@"%ProgramFiles%\MyCompany\MyApp",
new File(@"bin\Release\net6.0\win-x64\publish\MyApp.exe")));
project.GUID = new Guid("A1B2C3D4-..."); // 必须设 GUID,否则每次构建视为新产品
project.OutFileName = "MyApp-Setup";
project.BuildMsi();别忽略 UAC 和运行时依赖这两个硬门槛
WinForm 安装包默认以普通权限运行,但很多场景需要管理员权限(比如写注册表、装服务、放文件到 %ProgramFiles%)。WiX 默认不提权,必须显式设置 project.InstallScope = InstallScope.perMachine;,否则安装成功却找不到快捷方式或桌面图标。
另一个常见坑是 .NET 运行时没装——你打包的是 self-contained,但用户机器若没装过 VC++ 运行库(尤其是 vcruntime140.dll),启动时会直接报错:The code execution cannot proceed because vcruntime140.dll was not found.
解决办法:
- 在 WiX 项目中添加
new ExePackage("vc_redist.x64.exe"),从微软官网下载对应版本的vc_redist.x64.exe并嵌入 - 或者改用
Bundle(即.exe引导包),先静默安装 VC++,再运行 MSI - 测试务必在干净虚拟机里做,别只在自己开发机上点一下就认为 OK
调试安装过程:看日志比猜错误快十倍
MSI 安装失败时 UI 往往只显示“安装失败”,根本看不出哪一步挂了。必须加日志参数手动触发:
命令行运行:msiexec /i MyApp-Setup.msi /l*v install.log
日志里重点关注这几类行:
-
Return value 3表示自定义操作失败(比如你写的CustomAction抛异常了) -
Failed to find DLL或LoadLibrary failed多半是 VC++ 或 .NET 运行时缺失 -
Property: TARGETDIR = C:\这种说明路径变量没正确解析,常因Dir构造时用了相对路径或环境变量拼写错误
WiX 的 Heat.exe 工具能自动生成文件列表,但容易把 bin 下的临时文件也扫进去,建议人工精简 Dir 内容,只保留 publish 目录下的真正产出。
复杂点在于:每个 WinForm 项目的资源、配置、依赖结构都不同,没有“一键打包”能绕过路径校验、权限判断和运行时兜底。最省事的方式,其实是放弃 MSI,改用 Squirrel.Windows 或 MSIX——但那又是另一个部署范式了。


















