简介:Advanced Installer 22.5 是专业的 Windows 安装包制作工具,本资源正是围绕该版本整理的完整安装与项目文件包,面向需要在本地部署、学习或维护安装包制作环境的开发者与运维人员。包内共2000个文件,压缩后约229.56MB,其中以 aip 项目模板、png/jpg/ico 界面与图标素材、xml/xsd 配置定义、msi/exe 安装程序、rtf/html 文档及多语言界面资源为主,覆盖从图形界面定制、快捷方式与注册表配置、静默安装脚本到最终产出安装包的常见环节;同时包含一定数量的 bmp/svg/xaml 视觉资源、cfg/aitemplate 配置与模板、ps1/cmd 辅助脚本等,目录结构清晰,便于按需检索复用。已有1428人学习下载,对于希望离线搭建 Advanced Installer 环境,或需要参考标准打包流程、复用默认模板与素材的开发者而言,这份资源能帮助快速理解安装包结构与配置逻辑,减少重复配置工作。
1. Advanced Installer 22.5 打包 Windows 安装包:先想清楚它替你解决了什么
被安装包反复折磨过的人,应该都经历过这个场景:交付一个带 Windows 服务的小工具,压缩包发过去,对方解压后双击 exe 没反应,注册表里多出几个不知道谁写的键,卸载时更是删不干净。换了 Advanced Installer 22.5 之后,这些问题大部分不再是玄学——它把散装的 exe、dll、配置文件、Windows 服务和注册表项,统一编排成标准 MSI 或引导型 EXE 安装包,装的时候走 Windows Installer 引擎,卸的时候按记录回滚。这篇文章面向手里拿着 Windows 桌面程序的开发、运维或技术支持人员,讲清楚这个工具怎么用、参数怎么设、以及我在实际出包时踩进去又爬出来的坑。
2. 建工程前先选型:MSI、EXE 引导包与 MSIX 的边界
打开 Advanced Installer 22.5,第一屏不是让你填产品名,而是选 Project Type。很多人在这里凭感觉点了一个,后面才发现装进域环境费劲、命令行静默装不了、升级策略对不上。选型这事值得先说透。
2.1 三种工程类型怎么选:只看第一个安装场景就足够
我见过一个团队把内部小工具打成 MSIX,结果分发时要额外配置 App Installer,签名的坑还绕着走了两周。拿来做对比,三种格式的边界其实很清晰:
| 类型 | 适用场景 | 命令行控制 | 对系统环境要求 | 门槛 |
|---|---|---|---|---|
| MSI | 企业内部工具、需要组策略分发、需要精确卸载记录 | 完整支持 msiexec 参数 | 仅要求 Windows Installer 服务正常 | 低 |
| EXE(Bootstrapper) | 需要把多个安装包串起来、要自定义品牌界面 | 依赖引导器怎么封装 | 通常兼容性最好 | 中 |
| MSIX | 面向商店/企业托管平台,有强沙箱与签名要求 | 有限 | 需要较新 Windows,部署链路较长 | 高 |
我一般默认选 MSI。原因是它把安装台账交给 Windows Installer 管理,用户中途取消、安装失败、卸载残留这些事,引擎都有记录。Advanced Installer 的 EXE 引导包在我的场景里只用来做两段式安装:外层 EXE 先检测 .NET 运行库或者 JDK,缺了就装依赖,再调用内层 MSI。这样既能享受 MSI 的记账能力,又能替用户把前置依赖解决掉。
这里有个容易犯的毛病:把 EXE 理解成「从文件夹里拷出来打个包就叫 exe」的自解压程序。自解压只是把文件放出来,什么都不写进系统台账,卸载时只能靠遍历目录删文件。Advanced Installer 的 EXE 是 Bootstrapper,二选一时先问自己:目标机器上是否缺前置运行库?不需要就 MSI,需要再用 EXE 包一层。
2.2 从空白工程到能跑的 MSI:必须改的三个属性
新建工程时选「Simple」模板,22.5 会生成一个最小 MSI 工程。左侧栏从上到下是 Products、Media、Files and Folders、Shortcuts、Registry、Services 这几类页面,整个打包逻辑就是告诉它「文件放哪、注册表写哪个根、服务叫什么名、快捷方式建哪儿」。
建完工程先改三个地方,缺一个后面都会翻车:
第一步打开 Product Details 页面,把 Product Name、Manufacturer、Product Version 填对。Product Version 是四位点分版本号,首位不能超过 255。这不是 Advanced Installer 的限制,是 Windows Installer 的版本规则,写成 256.1.0 会在构建时报版本号非法。
Product Code 这一栏让工具自己生成即可,它是这个安装实例的唯一身份。但 Upgrade Code 需要慎重:一个产品线从头到尾保持同一个 Upgrade Code,后面做大版本升级才能被识别为「旧版本」。如果每个迭代都重新随机生成 Upgrade Code,MSI 就认定这是两个不相干的产品,升级时旧版本不会被处理。
安装类型选 per-machine 还是 per-user:内部工具一律 per-machine,装在 Program Files 下,对机器上所有用户可见;per-user 装进 %LOCALAPPDATA%,同一台机器另一个账号登录就要重装一次。
验证配置没问题,直接 Build。构建完的 MSI 默认在 Output 目录里。我习惯先把目标机器恢复到干净状态,再手工验证一次安装。
2.3 用 msiexec 验证第一次构建:静默参数与完整日志
图形界面双击安装只是验证了「能弹窗」,验证不了无人值守场景。我每次构建完必跑一条静默安装命令,顺手把日志抓全:
msiexec /i myapp.msi /qn /l*v install.log/i表示安装,/qn是完全静默,不弹任何窗口;/l*v是输出详细日志到 install.log。*代表 verbose 级别,v代表带详细信息。这条命令执行完,退出码 0 表示安装成功,其他值都能在日志里找到失败点。
装完再验证卸载:
msiexec /x myapp.msi /qn /l*v uninstall.log这里有个细节:MSI 卸载时也会跑一遍日志,而且卸载日志比安装日志更容易暴露问题,比如组件忘记注册、文件解锁失败。把这两个命令放进自己的验证清单,比在图形界面点十遍 Next 都管用。
注意:如果目标机器上曾经装过同名但不同 ProductCode 的版本,卸载命令必须指定具体产品代码,否则 msiexec 会提示找不到产品。需要时用
msiexec /x {GUID}精确卸载。
3. 把文件、注册表和 Windows 服务装进去:三块最常翻车的配置
3.1 文件目录:安装路径用 INSTALLDIR 还是默认目录
在 Files and Folders 面板里,工程会预置一个 Application Folder,默认映射到[ProgramFilesFolder][Manufacturer][ProductName]。绝大多数情况下这个路径可以直接用,理由是企业内部工具不需要给用户选择安装目录的权利,固定路径反而方便后续维护和脚本引用。
如果你确实需要让用户自定义安装目录,做法是选中 Application Folder,在属性面板里重定义 INSTALLDIR 属性,再在 UI 对话框里加一个目录选择框。这里我要提醒一个最常见的坑:用户改了安装路径,但你的服务配置和快捷方式参数里写死了绝对路径,安装完服务起不来。凡是引用安装目录的地方,全部要用[INSTALLDIR]这个属性占位,而不是硬编码路径。
文件级联也要注意。把一个版本的程序文件夹整体拖进 Application Folder 时,22.5 会保留原文件夹层级。如果里面混着 Debug 符号文件或者临时文件,一起打进去会让安装包体积变大、卸载时多一堆无用的记录。我每次拖文件前先做一次目录清理,砍掉 pdb、cache、日志这类运行时产物。
关于权限:per-machine 安装到 Program Files 时,安装过程会触发 UAC 提升。写入 Program Files 下的文件默认没有修改权限,这对大多数只读程序文件是对的。如果程序运行时要写配置到自己的安装目录,请立刻停止这个设计——把配置放到 ProgramData 或 AppData,避免权限边界问题。
3.2 注册表:HKLM 与 HKCU 的选择依据
Registry 页面里可以按新建键值的方式添加注册表项。填表时有三个字段:Root(根键)、Key(路径)、Name/Value(名称和值)。
选 Root 的判断依据只有一条:这个配置是机器级的还是用户级的。服务要读的连接串、进程要加载的全局开关,放 HKLM 的SOFTWARE\YourCompany\YourApp;纯界面偏好、最近打开文件这类个人设置,放 HKCU。HKCU 的好处是卸载时跟随用户配置自动清理,不需要额外逻辑;HKLM 则必须靠 MSI 的组件记账来确保删除。
Advanced Installer 里用 Registry 原生表配置,卸载时 Windows Installer 会把安装时写的键值自动回滚掉。这是它比我之前习惯的「自定义动作里 reg add/reg delete」高明的地方——脚本删注册表一旦路径写错,卸载就会往系统关键位置下手,风险太大。
有一类场景走原生表解决不了:运行时才决定写哪个注册表路径。这时只能上自定义动作,但自定义动作默认在系统上下文跑,权限和路径变量都要自己处理好。我的原则是:能用 Registry 面板表达的就用面板,绝对不手写注册表脚本。
3.3 Windows 服务:启动类型、账户与「起不来」的根源
带服务的程序,安装包的核心难点全在服务配置上。在 Services 页面新建服务,需要填这些字段:
| 配置项 | 推荐值 | 说明 |
|---|---|---|
| Service Name | 程序内部名称 | 与代码里 OpenSCManager 用的名称一致 |
| Display Name | 中文/易读名称 | 显示在 services.msc 里 |
| Start Type | Automatic(随系统启动) | 内部工具普遍用自动启动 |
| Start Account | LocalSystem 或 NetworkService | 见下方分析 |
| Dependencies | 对应依赖服务名 | 服务启动顺序依赖 |
账户选择是起不来的第一大原因。LocalSystem 是本地最高权限,能访问本地几乎所有资源,但访问网络共享时是以机器身份出面,域环境下会受限;NetworkService 类似,访问网络仍受 AD 策略管理;专用域账户适合需要访问数据库、需要特定权限的服务,但要把账户密码以参数方式传入安装包。
服务起不来时,第一件事不是回来调安装包,而是去 Windows 事件查看器里翻 System 日志,Source 写 Service Control Manager 的那几条就是答案。常见的两种情况:服务路径写成了[INSTALLDIR]字面量,没有解析成真实目录;或者服务依赖的 dll 不在服务可搜索的路径里。前者检查服务 binPath,后者检查 PATH 环境变量是否在安装时写入了。
提示:服务若依赖网络地址,比如启动时要连数据库,建议 Start Type 选 Automatic(Delayed Start)。否则开机时网络栈没就绪,服务启动直接超时,再去看它已经是停止状态。
3.4 快捷方式与文件关联:装完能让用户找得到入口
Shortcuts 页面把「桌面图标」「开始菜单图标」指向 Application Folder 里的主程序。这里有两个值得注意的点。第一,桌面图标对企业批量部署往往是多余的,却在卸载时又占一条记录,我通常只建开始菜单快捷方式,避免用户桌面被塞满。第二,主程序是 32 位还是 64 位,快捷方式的目标固定用[INSTALLDIR]属性,不能因为当前测试机上路径一样就写成绝对路径。
如果程序涉及打开特定扩展名的文件,文件关联也是在 Shortcuts/Files Associations 里做。关联的默认动作命令同样用属性占位,否则用户改了安装目录,双击文件就会报「找不到程序」。
4. 升级包比首版难十倍:版本规则、Upgrade Code 与卸载回滚
做首版安装包,多数人半天就能跑通。真正检验对 MSI 理解深度的,是第二版、第三版升级包。升级做不好,旧文件残留、两个版本并排、卸载时互相打架,全来了。
4.1 小版本原地升级与大版本重装:Product Code 和 Upgrade Code 的分工
先把两个 GUID 的职责说清楚。Upgrade Code 是整个产品线的身份证,永远不变;Product Code 是当前这个安装实例的身份证,大版本升级时可以变,小版本升级时可以不变。
在 Advanced Installer 的 Upgrades 页面里,点 Add Upgrade,配置新版本的版本号范围和升级策略。它会自动找目标机器上已安装的同 Upgrade Code 产品,决定是原地覆盖还是先卸载再安装。
| 升级场景 | Product Code | Upgrade Code | 结果 |
|---|---|---|---|
| 小修小补 | 保持相同 | 不变 | 原地覆盖,保留用户配置 |
| 大版本(不兼容) | 重新生成 | 不变 | 旧版记录替换,装新版 |
| 误改 Upgrade Code | 随意 | 变了 | 两个版本并存,旧版卸载成谜 |
我最开始做大版本升级时,习惯性地把 Product Code 和 Upgrade Code 一起重新生成,结果用户机器上出现「旧版还能从控制面板卸载,新版也装上了」的奇观。从那以后我对 Upgrade Code 的态度是:代码里定义一个常量,构建脚本不生成它,格式化也不许动。
4.2 文件版本号与 REINSTALLMODE:为什么升级完旧 dll 还在
升级后目标机器目录里还有旧版 dll,十有八九不是安装包没起作用,而是文件版本规则把旧文件「保留保护」了。Windows Installer 在覆盖文件前会对比新文件版本、语言和哈希。新版文件版本号如果小于或等于已安装的版本,引擎认定「没有需要更新的文件」,跳过覆盖。
解决办法是先检查自己的输出文件有没有正确写入 Assembly/File Version。很多开发机的构建流程里,版本号写死在 csproj 或版本头文件里,升级时忘了改,打出来的 dll 还是旧版本号。文件版本改了仍然不覆盖,再检查升级策略里的 REINSTALLMODE。我一般会在升级策略中显式设置REINSTALLMODE=amus:
- a:强制覆盖所有文件
- m:使用新版文件替换旧版
- u:补齐缺失文件
- s:校验哈希
在 Advanced Installer 的 Upgrades 设置里把升级行为勾选为「Replace older version」,再配合文件版本号递增,旧文件残留的问题基本消失。注意 REINSTALLMODE 是 MSI 引擎级别的参数,不是工具自定义的开关。
4.3 卸载与回滚设计:让 MSI 自己清算,别手写删除逻辑
卸载时哪些文件被删,完全由组件(Component)记账决定。你在 Files and Folders 面板里安排的文件,构建时被分配到一个组件,卸载时组件计数归零就整体删除。明白这一点,就不要在自定义动作里写「删除用户配置文件」的脚本。
用户配置保留策略要做的是另一件事:程序运行后把配置写在安装目录外的位置,比如 ProgramData。卸载时 MSI 只清理装进 Program Files 的那部分,运行数据保留是预期行为。如果你非要卸载时询问用户「是否保留配置」,可以在卸载流程里加一个条件自定义动作,弹一个 Message Box,而不是直接把删除路径写死。
安装失败的自动回滚也是 MSI 机制的一部分。安装到一半出错,引擎会按逆向顺序撤销已完成的复制和注册表写入。这里要防的是「延迟提交型」自定义动作:安装实际没成功,但自定义动作已经把状态写进了系统环境变量,回滚救不回来。我现在的原则是,自定义动作只做安装主流程之外的事,且必须允许失败,不要把业务逻辑挂进安装事务里。
5. 避坑实测记录:装不上、服务起不来、旧文件残留的五条排查路径
以下五条全是实打实遇到过的现场,每条按现象、原因、解决三个步骤拆开。这些问题在官方文档里都找得到,但真正出事时没人翻文档,有份排查清单会快很多。
5.1 现象一:双击 MSI 没反应,任务管理器里一闪而过
双击后安装界面没出现,任务管理器里 msiexec 进程闪一下就消失。原因可能是系统里已经有一个 MSI 安装事务在排队,Windows Installer 全局串行,前一个没结束,后一个直接退出;也可能是 SmartScreen 拦截但没弹提示。解决:先看事件查看器的 Windows Installer 日志,确认有没有 「Another installation is already in progress」;再用命令行执行一遍msiexec /i myapp.msi /l*v install.log,日志会直接把失败原因写清楚,比盲改工程靠谱得多。
5.2 现象二:服务装上了,启动那一栏是错误
安装过程没有报错,服务也注册进了 services.msc,但启动按钮点下去马上失败。原因排查顺序:第一看事件查看器里 Service Control Manager 的报错编号,第二看服务的「登录」选项卡是不是用本机账户在跑,第三确认 binPath 里的安装目录路径有没有解析正确。我遇到最多的是第三种——服务配置里写的路径带上了[INSTALLDIR]字面量,安装时没有被替换成真实路径。解决:在 Services 页面重新选择主程序文件作为服务可执行文件,不要手打路径;手打的路径构建时不会做属性解析。
5.3 现象三:升级完旧 DLL 还在安装目录
用户跑完升级包,检查安装目录,发现旧版 dll 和配置文件还在。原因可能是文件版本号没递增,Windows Installer 根据文件版本决定是否覆盖;也可能是升级策略没有设成「替换旧版本」。解决:先确认输出文件的版本号确实变了,再按前面说的设置 REINSTALLMODE=amus。文件版本号验证可以在 PowerShell 里跑(Get-Item .\myapp.dll).VersionInfo.FileVersion,别靠文件名猜。
5.4 现象四:执行 msiexec 报图 1618 或「另一个安装正在执行」
多台目标机器同时推安装包,或者同一台机器上连续跑两个安装包,就会遇到这个错。原因是 Windows Installer 的全局互斥,同一时刻只允许一个安装事务。解决:要么等上一个安装进程结束,要么在分发工具里做队列串行。我在 CI 里出包后做安装验证时,会先把可能残留的 msiexec 进程清干净,再跑新的安装测试。
5.5 现象五:卸载后注册表里还能搜到产品路径
控制面板卸载完,注册表里依旧能找到产品名。这个现象要分情况看:如果你在 HKLM 里写的键是 MSI 组件管理的,卸载就会自动删;如果你的自定义动作或程序安装后又往 HKLM 写了新键,卸载时没人替它擦屁股。解决:排查注册表里的残留键是谁写的,原生表配置的交给 MSI,程序运行时写的那部分,要么在设计上改成 ProgramData 文件,要么在卸载界面里提供「清理用户数据」选项,让用户决定是否删除。
6. 把 22.5 的构建从界面搬到命令行:CI 出包脚本
6.1 我留在 Jenkins 里的出包脚本
图形界面构建适合开发期,一到正式发布,我坚持用命令行。Advanced Installer 提供了独立命令行工具 AdvancedInstaller.com,位置在安装目录下。先确认构建机上这个文件存在,并把它加进系统 PATH。只要 .aip 工程里已经把输出格式、输出目录、版本信息都配好,命令行就只是触发一次构建:
AdvancedInstaller.com /build myapp.aip构建完后按退出码判断结果,非零就是失败。需要临时改版本号时,我用一个参数覆盖工程里的版本值,这样 CI 里每次构建都能用日期或构建号当版本号,不用手工改工程文件:
AdvancedInstaller.com /build myapp.aip -SetVersion 2.4.0不同小版本对命令行参数命名略有差别,构建机上先跑一次AdvancedInstaller.com /?看头几行帮助,确认当前版本的参数写法,这一步每次升级工具版本后都要做。构建时如果弹出许可证激活窗口,检查构建机有没有把许可证授权信息配置完整——CI 环境下最怕安装完工具后忘了输许可证,导致自动化出包卡在人机交互上。
我在 Jenkins 里把它做成一个 Pipeline 步骤,构建产物按产品名加日期放到共享目录,然后触发安装虚拟机做 msiexec 验证。从那以后,每次出包我都强制走一遍「命令行构建 → 干净机器静默安装 → 查日志 → 升级测试 → 卸载测试」这个流程,没有再手工点过下一步。如果你是第一次接触 22.5,这五个环节可以先用图形界面跑通,再逐步迁到命令行,出包的节奏会稳很多。希望帮到你。
本文还有配套的精品资源,点击获取