简介:Advanced Installer 20.7.1软件打包工具是一款面向Windows平台MSI安装包制作的专业资源,适合软件开发人员、打包工程师以及需要规范化软件分发的IT人员。该工具提供可视化配置界面,支持自定义欢迎页面、安装进度页面,并可生成符合微软Windows认证的MSI文件。资源包以zip压缩包形式提供,共2000个文件,以aip工程文件为核心,辅以图片素材、配置文档、说明文档以及8个msi样例包,整体约161.36MB。包内还整合多语言本地化界面资源(如ail文件)和完整素材,方便对照模板学习安装包制作流程,也适合用于界面定制与二次开发。已有941人学习下载,适合希望在Windows分发打包环节提升效率的开发者快速上手。
1. 软件打包不是 zip:Advanced Installer 20.7.1 解决的是安装体验和卸载残留
很多团队交付 Windows 桌面程序时,还在用压缩包加一个“自述.txt”。但当你面对的是企业客户、内网分发或需要替用户装好服务、驱动、启动条件的场景,压缩包完全不够用——用户解压后不知道点哪个 exe,缺少 .NET 运行库直接闪退,卸载时注册表项还留在机器里。Advanced Installer 20.7.1 是一款 Windows 安装包制作工具,它把 MSI / EXE 构建、启动条件、多语言、数字签名和命令行自动打包整合在一个 GUI 工程里,解决了“安装体验”和“卸载残留”这两件压缩包干不了的事。这篇笔记适合要交付 Windows 软件的开发、运维和打包岗,目标是让你从建工程一路做到能放进 CI 的自动构建。
2. 把第一个产品装进 MSI:新建工程到文件映射的三步配置
2.1 选对工程类型:Advanced Installer 里四种工程模板的分工
首次打开 Advanced Installer 20.7.1,向导会让你选工程类型。常见选项有 Professional、Enterprise、Architect 等授权级别,同一级别下还有按安装场景分的模板:Simple、MSI from EXE/MSI、Repackager、AppX/MSIX。新手最容易犯的错是一上来选 Simple 然后猛加文件,结果发现升级、条件、命令行参数全都锁着,因为 Simple 模板默认走的是 EXE 引导包而不是标准 MSI。
我一般建议用 Advanced Installer 的“Product”类型工程,输出目标先设为 MSI。它对应的是 Windows Installer 标准格式,能被域策略、System Center、第三方分发平台直接识别。对于项目刚起步的团队,先拿标准 MSI 把链路跑通,后续再按交付方式加 EXE 引导或 MSIX 变体,而不是反过来。
2.2 产品属性与版本号的设定:给后续升级留好退路
新建工程后第一件事不是拖文件,而是进“Product Details”把四个字段填对:Product Name、Product Version、Product Code、Upgrade Code。前两个用户看得见,后两个是系统级标识,直接影响升级与卸载。
- Product Code:每个版本都应当不同,等价于 Windows Installer 眼中的身份 ID。大版本每次变化是常态。
- Upgrade Code:一个产品家族的公共 ID,跨版本保持不变。后续升级包通过它识别老版本,从而执行“卸载旧版再装新版”或“覆盖更新”。
- Version:格式建议用主版本.次版本.修订号,例如 2.4.1。热修复时只升最后一位,且文件名和版本号保持一致,否则服务器上的安装记录对不上。
这里埋了打包工作中最常见的坑:改了 Product Code 却忘了 Upgrade Code 要保持一致,或者反过来把 Upgrade Code 也改了,结果是新版本在另一台机器上跟老版本并存,注册表项互相打架。我自己的惯例是把这四个属性写进一个版本管理表里,每次发版时跟提交记录一起改。
2.3 文件和文件夹映射:一个 GUI 操作背后对应的目录表结构
在“Files and Folders”页面里,拖拽文件到左侧的“Application Folder”。这看起来像普通文件复制,实际上它同时干了两件事:把文件登记进 MSI 的 File 表,并为文件生成一个组件 GUID。卸载时,Windows Installer 按组件引用计数决定是否删除文件,这就是 MSI 包卸载相对干净的根本原因。
需要注意的细节有三个。第一,默认安装目录建议设为“ApplicationFolder”[INSTALLDIR],绝对路径如 C:\Program Files\MyApp 会导致 64 位 / 32 位系统、多用户环境全部按死路径装,后面无法做每用户安装。第二,不要把配置文件跟程序文件混在同一个目录;配置文件放进 CommonAppDataFolder(对应 C:\ProgramData)会让升级包覆盖数据的概率大幅降低。第三,文件版本号冲突时,Advanced Installer 会在构建日志里报告 File table 中相同的 KeyPath,这个时候回头检查是否有两个组件同时声明了同一个文件。
另一个容易忽视的点是“TARGETDIR”之外的自定义目录。如果你在 UI 里加了一个“数据目录”让用户在安装时选,那么在 Files and Folders 里一定要给该目录建一个独立组件,否则卸载时目录残留成空壳。Advanced Installer 在“Components”页会对这类自动生成的组件标注状态,构建前扫一眼是否有醒目高亮的 component 再继续。
3. 安装逻辑的两个重头戏:启动条件与配置项
3.1 启动条件:检查 .NET、VC++ 运行库、Windows 版本
安装包最容易翻车的地方不是文件拷贝,而是装完之后程序起不来。Advanced Installer 20.7.1 把启动条件放在“Launch Conditions”下,支持按“搜索目标机器”的方式来判定:Windows 版本、.NET Framework、PowerShell、SQL Server、IIS、注册表值、文件是否存在。
拿最常见的 .NET 依赖来说,我的做法是加一个“Search for .NET Framework”,让它查注册表项SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full的 Release 值,再在条件里写 Release >= 533320 表示要求 4.8.1。如果客户机器没装,我希望它弹提示或跳转下载页,就要再配一个“Display message and abort setup”。
这里有个判断条件语法的坑:Advanced Installer 默认走的是 Windows Installer 属性判断,布尔表达式大小写敏感。曾经因为条件里写VersionNT64 = 600误判,导致 Win7 64 位机器一直报“系统版本不支持”。建议先在目标机器上用msinfo32或 PowerShell 的Get-ComputerInfo确认实际操作系统版本,再做判定。
3.2 快捷方式和注册表:安装时写、卸载时擦干净
在“Shortcuts”页面建快捷方式,目标文件选“Application Folder”里的主程序,再选“desktop”或“Start Menu”目录。一个常见误操作是把快捷方式目标写成绝对路径,比如 C:\Program Files\MyApp\app.exe,这样用户改了安装目录之后,快捷方式的指向就停留在旧路径而失效。应该始终使用“Application Folder”加文件名这样的相对引用,让 Advanced Installer 在安装时解析成实际目录。
注册表部分要分清 HKCU 与 HKLM。每用户安装写 HKCU,机器级安装写 HKLM。在“Registry”页面里点击右键新建注册表项,值名和类型要跟程序读取时完全对应——尤其是 REG_DWORD 与 REG_SZ,写错类型会让程序读取时直接忽略默认值或抛异常。卸载时,Advanced Installer 会根据注册表项的归属自动撤销它自己写的内容,但对“自己创建的注册表项”之外的内容它不会碰。如果你在安装脚本里用自定义动作修改了系统已有键值,卸载逻辑需要自己补一条回滚脚本,否则残留就留下了。
3.3 服务、驱动与权限抬升:三类最容易翻车的资源处理
“Services”页面允许你在安装包内直接注册 Windows 服务。这里的关键参数是启动类型、服务账户和依赖关系。服务账户选 LocalSystem 最简单,但如果服务要访问网络共享,则需要域账号或托管服务账户。依赖服务如果没装,服务启动时会在事件管理器报错,排查起来非常费劲。我一般会在启动条件里提前检查依赖项,而不是等服务启动失败再去对日志。
驱动(Driver)的安装常常被忽略。普通安装包想装驱动,Advanced Installer 会要求管理员权限并生成对应的 INF 安装逻辑。这里要提醒一句:并不是所有驱动都适合在 MSI 里直接装,硬件厂商提供的驱动包(含 .sys 和 .cat 签名)建议保留为前置依赖,通过“Prerequisites”里配置独立 EXE,而不是手动把 sys 文件拷到 System32,否则签名验证和驱动哈希会变,导致系统拒绝加载驱动并要求重新签名。
权限抬升这块,在“Prerequisites”和“Custom Actions”里都涉及 UAC 问题。MSI 默认继承系统的 UAC 行为,如果程序写 Program Files,安装时必须请求管理员权限。Advanced Installer 里可以设置安装包的 requestedExecutionLevel 为 requireAdministrator。但要注意:一旦设成 requireAdministrator,双击 EXE 的普通用户必然触发 UAC,这会给内网批量部署带来交互障碍。更稳妥的分发路径是把安装包交给 SCCM 或域脚本以 SYSTEM 身份运行,安装包自身保持默认的“highestAvailable”,由调用方控制提权。
4. 构建 EXE 与多语言分发:从单机走向交付
4.1 MSI 与 EXE 包的分工逻辑:什么时候必须出 EXE
Advanced Installer 的常见输出方式是把 MSI 包再套一层 EXE 引导,目的是处理前置依赖(.NET、VC++ 运行库)或做多包串联。MSI 本身不支持在安装过程中联网下载或快速判断运行库版本,而 EXE 引导可以。如果你交付的客户端只有几个 DLL,直接输出 MSI 即可;如果程序强依赖 VC++ Redistributable 或 .NET Desktop Runtime,就应当把运行库作为前置项放进 EXE 引导。
构建 EXE 后,安装规模会大不少,因为运行库包被嵌入或下载到本机缓存。我的经验是:EXE 引导出来的安装界面和安装顺序是可预测的,先是引导器自身,然后按依赖顺序逐个装,适合面向非技术用户的交付;而 MSI 更适合已部署了统一运行库镜像、追求全静默安装的批量环境。
4.2 升级与补丁:同一个 UpgradeCode 是底线
按 2.2 节的说法,UpgradeCode 不变,新的 MSI 才能识别旧安装。在构建时勾选“Upgrade”相关选项,Advanced Installer 会基于 UpgradeCode 搜索旧版本,然后执行替换。这里必须指定“Remove older versions”或“Keep older versions”,两种策略的差异在于程序和配置文件的保留方式。
补丁(Patch)是另一层逻辑。导出 MST 文件(转换文件)可以在不重新分发整个 MSI 的情况下调整少量配置,但它要求用户已经安装过原版。给补丁做数字签名时,签名证书要与原 MSI 的签名一致,否则部分系统会拒绝加载补丁。如果补丁做出来是 msp 格式,分发时推荐配合标准安装脚本做静默安装,避免用户双击后误选卸载选项。
4.3 多语言与本地化:安装界面和帮助文件的处理
Advanced Installer 的多语言在“Languages”页面里勾选,每个语言对应一个不同的 UI 资源 DLL。它会自动把 Windows Installer 内置的对话框文案翻译成所选语言,但你自己加的软件许可协议文本需要在对应语言页单独导入。一个注意点是 Locale 列表里若同时包含中英文,默认语言不要随手选 English,最好按你的主要用户群体来定,否则海外机器装出来界面是中文,反而影响验收。
程序内部的多语言资源与安装界面无关,安装包只负责把对应的语言目录放进目标位置。需要在“Files and Folders”里按语言子目录区分放置,并在启动条件或用自定义动作根据“Message Box”或语言标识来决定后续文件复制逻辑。这块是多数打包工程师容易踩坑的地方——靠 UI 上选语言但忘了把 language folders 同步挂进来。
5. 打包现场常见问题排查:五个高发坑位与修复顺序
5.1 安装到一半回滚:日志先看 Windows Installer 的 verbose 输出
现象:客户反馈安装进度条到某个百分比突然跳回,提示“安装已中止”。原因多种多样:文件被占用、自定义动作退出码非零、启动条件不满足或权限不足。盲目重新打包等于黑匣子调试。
解决:先在命令行执行msiexec /i yourinstaller.msi /l*v install.log,拿到 verbose 日志后搜Return value 3或Rollback。Windows Installer 的日志会把每个动作的退出码和回滚点都打出来。一般最常见的是自定义动作(Custom Action)中的 PowerShell 脚本在 SYSTEM 账户下运行失败,或写日志路径不存在。修复方式是给自定义动作添加“仅在安装阶段运行”以及“忽略退出码”选项(如果动作失败不影响主安装)。
5.2 卸载残留与文件占用:文件映射表与组件 GUID 的检查
现象:用户卸载后,安装目录还是留下几个 DLL,或开始菜单快捷方式存在。
原因:这类残留大多是因为那几个文件没有独立组件,或文件映射时被归到“不属于你的组件”下。Advanced Installer 里若把两个文件塞进同一个组件,卸载时只要其中一个文件被引用,整个组件都不会删除。更隐蔽的是安装了新版后又装旧版,导致组件的引用计数叠加,卸载一次减不掉。
解决:在“Components”列表里逐一检查文件与组件是否一比一关联;不要手写组件 GUID,让工具自动生成。如有必须保留的用户数据文件,建议单独放到用户目录或 ProgramData,并在卸载脚本里用“变量 + 判断文件是否存在”来清理,而不是直接依赖 MSI 的组件删除机制。
5.3 缺少运行库导致启动即崩溃:预装依赖别用“扛过去”方案
现象:测试机装好包,双击主程序退出代码 0xc000007b 或提示找不到 VCRUNTIME140.dll。
原因:程序编译时用的是动态链接方式,而安装包没有把 VC++ Redistributable 打包进去。有人图省事把 vcruntime140.dll 直接放进安装目录,这在多数情况下能过,但系统目录里的相同 DLL 更全局,一旦程序引用了不同位数的同名 DLL,就会出现位数不匹配崩溃。
解决:把“Prerequisites”里的 Microsoft Visual C++ Redistributable 勾上,并选择下载或嵌入。嵌入会让包体变大,但离线环境内网分发更稳。注意 x64 与 x86 运行库要不要同时装,取决于你的主程序是 AnyCPU 还是强制勾选了 x86 模式。这里最容易翻车的场景是 MSI 直装模式下无法判断运行库版本,所以前面说“必须出 EXE”就是为这类场景准备的。
5.4 数字签名的时间戳:为什么本地测试正常、客户机器报警
现象:包在本机双击安装没问题,发给客户后 SmartScreen 显示“未知发布者”,企业安全策略直接拦截。
原因:多数情况是只做了代码签名但没有带时间戳服务器,证书过期之后签名验证失败;或签名所用的证书链中的根证书没在客户机器上。
解决:建议在 Advanced Installer 的签名配置里勾选“Add timestamp”,填一个可访问的时间戳服务器 URL,并用正规代码签名证书,而不是自签名证书。测试时用signtool verify /pa /v验证签名的完整链,不要只看文件属性里有没有“数字签名”页签。对客户内网机器而言,如果公司启用了强制签名策略,必须在客户环境验证根证书是否已由域策略自动推发。
5.5 64 位与 32 位混装:注册表重定向引发的玄学
现象:同一个安装包在 64 位系统上安装后,程序读取注册表 HKLM\Software\MyApp 读不到内容。
原因:32 位进程在 64 位系统中读注册表会被 WOW64 重定向到 HKLM\Software\WOW6432Node,而 Advanced Installer 默认按工程位数写注册表。如果主程序是 32 位但安装包配置成 64 位组件,就会写到原生的 32 位路径之外,两个环境互相找不到。
解决:建立工程时确认目标平台位数与主程序一致。如果必须做成单一安装包同时覆盖两种程序,在注册表添加步骤里手动添加两个注册表视图项(一个原生、一个 WOW6432Node)。检查时在目标机器上用reg query HKLM\Software\MyApp和reg query HKLM\Software\WOW6432Node\MyApp分别确认,别再靠眼睛看注册表编辑器——它自己会做重定向显示,容易被告假象误导。
6. 命令行打包与 CI 集成:把构建动作交还给版本控制
6.1 命令行参数一览:构建、日志、版本号覆盖
Advanced Installer 20.7.1 自带一个命令行工具 AdvancedInstaller.com,适合脚本化。和平时在 GUI 里点“Build”不同,命令行方式能把版本号、输出路径全部参数化。最简构建命令:
AdvancedInstaller.com /build installer.aip常用参数:
/build:指定构建目标,可以带-out指定输出文件名。/rebuild:强制整体重新生成,避免增量缓存导致文件版本不刷新。/SetVersion:覆盖工程里的版本号,适合 CI 里读取 Git Tag 后注入。/Log:构建日志输出路径,失败定位时日志直接落到指定文件。
在 Jenkins 或 GitLab CI 里,推荐把版本号统一写到环境变量,构建任务读取该变量调用/SetVersion,产物命名带对应版本号。不要每次打开 GUI 手动改版本再按“Build”按钮提交。那样很容易出现代码改了、安装包版本没改,或者 Debug 配置被误打进安装包的情况。
6.2 一键构建脚本的骨架:把三个动作串起来
一个可维护的打包流水线至少包含三步:清理旧产物、注入版本号并构建、校验产物签名与文件哈希。下面是一个 PowerShell 脚本骨架,模拟在 Windows 构建机上的执行顺序:
# 清理上一轮的构建输出,防止旧包混入 Remove-Item .\output\*.msi, .\output\*.exe -ErrorAction SilentlyContinue # 从 Git tag 读取版本号,例如 refs/tags/v2.4.1 -> 2.4.1 $version = $env:CI_COMMIT_TAG.TrimStart("v") # 构建 MSI 和 EXE 两个分发形态 AdvancedInstaller.com /build installer.aip /SetVersion $version -out "output\MyApp-$version.msi" AdvancedInstaller.com /build bootstrap.aip /SetVersion $version -out "output\MyApp-$version-setup.exe" # 校验主程序与安装包的签名,失败即中止流水线 Get-AuthenticodeSignature .\output\MyApp-$version-setup.exe | Select-Object Status, StatusMessage逻辑说明:第一段清理很关键,CI 机重复执行时若不清理,可能把上一轮成功构建的文件一起带进发布目录;第二段从 CI 变量取版本号,避免人工编辑版本号;第三段只构建,不放任何 GUI 交互步骤。这里的Get-AuthenticodeSignature只是基础校验,拿到结果后应当判断Status -eq "Valid"才继续发布。
签名动作本身建议放在构建之前,或在构建后单独执行signtool sign,而不是依赖 Advanced Installer 的 GUI 配置里缓存证书密码——证书密码在脚本里做成受保护的密钥变量更安全。最终产物在 CI 里打上构建时间戳和 Git 提交号,后续溯源会省掉无数沟通成本。
6.3 交付前的检查清单与安装验证
每次构建完成后,我会在干净虚拟机里跑一遍以下验证,把它当作发布前的最后闸口:
- 静默安装:
msiexec /i package.msi /qn /l*v installed.log,确认退出码是 0。 - 安装目录文件齐整:核对文件数量与构建前清单一致。
- 启动程序并检查进程可正常运行,无弹窗报错。
- 卸载后重启,检查安装目录、注册表、快捷方式是否均已移除。
最后一步常被我用来测试安装包对系统环境的侵入性。如果卸载后有目录残留,优先回到组件映射里看文件归属;如果注册表残留,看是不是自定义动作写了内容但没配回滚逻辑。
进阶用法上,我还习惯给每个安装包附一个包含 ProductCode、UpgradeCode、文件哈希的 manifest,放进构建产物目录。一旦后续要出补丁包或做差异更新,拿着新旧 manifest 对比就能直接判断组件级差异,不用再把整个 MSI 反编译开。这个习惯帮我省掉了好几次“明明改了文件版本却没被安装包带上”的血泪教训。希望帮到你。
本文还有配套的精品资源,点击获取