简介:资源为NSIS 3.0.4.1的7z压缩包,面向Windows平台软件开发者、安装包制作人员及运维工程师,用于构建免费开源、脚本驱动的专业Windows安装程序,尤其适合需要批量部署或个性化安装界面的场景。NSIS支持文件复制、注册表修改、环境变量配置、自定义交互界面以及多语言和数字签名,既能完成简单封装,也能满足复杂发布需求。压缩包约1.29MB,下载页未公布具体文件总数与类型明细,内容包含编译器makensis.exe、脚本模板、语言文件与常用插件等核心组件,解压后即可为makensis命令行编译提供完整环境。借助该工具可完成从编写.nsi脚本到生成.exe安装包的全流程,方便软件分发、升级与卸载。已有415人学习,适合希望快速搭建安装包工具链并熟悉NSIS脚本语法的用户,从入门到进阶均可受益。
1. 为什么是 nsis-3.0.4.1:一个离线程序包解决安装包构建的起步问题
前阵子做 Windows 桌面产品交付,同事丢给我一个 nsis-3.0.4.1.7z 程序包,说“解压就能出安装程序”。NSIS 是 Windows 平台最老牌的安装包构建工具,3.0.4.1 是 3.0.4 系列收尾的维护版本,一个 7z 带齐编译器、插件和官方头文件,哪怕完全离线也能构建安装包。适合手上有桌面程序、要定期产出 setup.exe,又不想被重型安装工程拖住的人。
它的学习曲线很平:打包逻辑全部集中在一个 .nsi 文本文件里,编译命令只有一行,改动分钟级生效,放进 CI 也顺手。下面按我自己的真实使用顺序来写:先让编译器跑起来,再写最小脚本,接着补齐注册表、快捷方式和静默安装,最后把这版最常见的坑和发布前的封装列一遍。每个环节都给能直接抄走的命令,新手照着走,熟手直接看参数边界和踩坑点。
2. 从 7z 到可用的编译器:解压验真、目录识别与 PATH 配置
2.1 解压先认目录:这包 7z 里关键组件各管什么
拿到包先别急着双击。这份 7z 解压出来是一个完整的 NSIS 3.0.4.1 目录树,不是“装上就能点”的图形工具。它由命令行编译器和一堆配套目录组成,认清楚这些目录后面排错才有方向。
| 目录 / 文件 | 作用 | 我的使用习惯 |
|---|---|---|
| bin\makensis.exe | 命令行编译器,真正干活的主体 | 打包脚本和 CI 都调它 |
| bin\makensisw.exe | 带界面的编译前端 | 只在临时看日志时用 |
| Include\ | 官方头文件,LogicLib、MUI2 等宏定义都在这里 | 只引用,不修改 |
| Plugins\ | 插件 dll,System、nsProcess 等按需加载 | 缺插件时编译期直接报错 |
| Stubs\ | 安装器运行时框架模板 | 保持不动 |
| Doc\、Examples\ | 官方文档和示例脚本 | 查语法时翻 |
这里有个关键机制:makensis 启动时会按它自己的 exe 路径去找 Include、Stubs 和 Plugins,也就是说 bin 目录必须是整个目录树的组成部分。谁要是把 makensis.exe 单独复制到别的文件夹,编译器立刻翻脸,报“找不到 stub”或者“include 找不到”。这个特性在第 5 章的踩坑实录里还会再见到。所以解压之后,先确认 bin、Include、Stubs 这三个是同级存在的。
2.2 动手前的验真:哈希比对与解压参数
从内网或同事手里拿到的 7z,我一般会先算一遍 SHA-256。这一步不是走形式,这包解压出来的东西要直接参与构建,哈希对不上就不要继续往下走了,换源重取是最稳的处理方式。
Get-FileHash .\nsis-3.0.4.1.7z -Algorithm SHA256 | Format-List这条 PowerShell 会把哈希值打印在屏幕上,拿它和官方发布页给出的 SHA-256 逐字符比对。注意 PowerShell 输出的是大写十六进制,如果发布页给的是小写,忽略大小写差异即可。
提示:哈希比对确认无误之后再解压。压缩包这种二进制载体,在传输过程里坏一个字节就是废包,哈希是对这个程序包负责的唯一手段。
解压我习惯用 7-Zip 命令行,方便在脚本里重复执行:
7z x nsis-3.0.4.1.7z -oD:\Tools\nsis-3.0.4.1参数解释:x是解压并保留目录结构,-o指定输出目录。输出目录我坚持用全英文且不带空格,一是避免后续批处理脚本转义出问题,二是 NSIS 3.x 虽然默认 Unicode 构建设计,但一些第三方插件对非 ASCII 路径的包容度不一,没必要把自己的环境放到这种风险里。
2.3 让 makensis 全局可调:系统变量与项目脚本两条路线
解压完成后,下一步是让命令行在任意目录都能叫到 makensis。这里我给两条路线,按环境取舍。
路线 A,写入系统 PATH,用 setx:
setx /M PATH "%PATH%;D:\Tools\nsis-3.0.4.1\bin"/M表示写系统环境变量,需要管理员权限。这条命令有一个暗坑:cmd 会先把%PATH%展开成当前完整值,再合并新路径写回去,如果系统 PATH 原本就接近 1024 个字符,setx 会把后面的内容截断。这是我在公司机器上见过最真实的翻车现场。所以执行 setx 之前,先echo %PATH%看一眼长度,太长就别用这条路。
路线 B,项目内放一个构建脚本,只在当前窗口临时生效:
@echo off set "NSIS_DIR=D:\Tools\nsis-3.0.4.1\bin" set "PATH=%NSIS_DIR%;%PATH%" makensis /V3 /WX installer.nsi这种做法的好处是环境变量不污染系统,不同项目可以各自指向不同版本的 NSIS。/V3把编译输出开到比较详细的级别,会显示警告信息;/WX把所有警告当作错误直接中断编译,我出正式包时必开,它能让潜在问题在打包阶段就暴露,而不是等安装现场出故障。
3. 最小可安装脚本:Section、Page、变量与逻辑的组合
3.1 Section 与 SectionGroup:安装块怎么划分
NSIS 的安装行为全部写在 Section 里。一个 Section 就是一块安装功能,用户可以对应勾选或取消。写第一个脚本时,先别追求界面,让安装器能生成就行。
Name "DemoApp" OutFile "demo-setup.exe" InstallDir "$PROGRAMFILES64\DemoApp" RequestExecutionLevel user Section "主程序" SEC_MAIN SetOutPath "$INSTDIR" File /r "payload\*.*" WriteUninstaller "$INSTDIR\uninstall.exe" SectionEnd Section "命令行工具" SEC_CLI SetOutPath "$INSTDIR\bin" File "cli\demo-cli.exe" SectionEnd每个 Section 内第一条命令通常是SetOutPath,指定文件释放目录。File /r "payload\*.*"把 payload 目录下的所有文件和子目录按相对结构释放到$OUTDIR;$INSTDIR是最终安装目录,用户在目录页可以直接改。WriteUninstaller生成卸载程序,这是后面卸载注册表条目的基础。
如果组件多,需要用户按组选择,就用 SectionGroup 包起来:
SectionGroup "开发组件" Section "SDK 头文件" SetOutPath "$INSTDIR\sdk" File /r "sdk\*.*" SectionEnd Section "示例工程" SetOutPath "$INSTDIR\examples" File /r "examples\*.*" SectionEnd SectionGroupEnd分组只影响组件页的树形展示,安装逻辑上仍然是独立 Section。给 Section 起名时用可读性强的名字,后面用${SectionIsSelected}做条件判断时,代码会好维护得多。
3.2 ${If} 与 Var:把运行时判断写进脚本
写简单安装脚本可以不用条件判断,但真实产品迟早会遇到“静默模式下不弹窗”“检测到旧版先执行清理”之类的分支。NSIS 的条件判断用官方 LogicLib 宏,它不在默认加载范围,需要先!include "LogicLib.nsh"。
!include "LogicLib.nsh" Var InstallMode Function .onInit StrCpy $InstallMode "normal" ${If} ${Silent} StrCpy $InstallMode "silent" ${EndIf} FunctionEnd Section "安装" ${If} $InstallMode == "silent" SetDetailsPrint none ${EndIf} SetOutPath "$INSTDIR" File "app.exe" SectionEnd${Silent}是 LogicLib 内置的静默判断,/S运行时它为真。SetDetailsPrint none在静默安装时关掉详细日志输出,避免在无界面环境下刷屏。变量必须先Var声明再StrCpy赋值,NSIS 变量是全局字符串,并且大小写敏感,$INSTDIR写成$InstDir直接报 Unknown variable,没有商量余地。
.onInit函数在安装程序启动时执行,适合放初始化和参数预判断;卸载对应的un.onInit同理。我的习惯是把所有需要分支的状态变量集中在初始化函数里赋值,后续 Section 只读变量做判断,这样脚本长时间维护之后自己还能看懂。
3.3 Page 与 MUI2:从裸界面换成完整向导
不引入任何头文件的裸脚本,只用Page directory让用户选目录,Page instfiles显示安装进度。功能是能跑,界面观感像十几年前的老工具。想让安装包像正式产品,用 MUI2 是标准做法。
!include "MUI2.nsh" !define MUI_ABORTWARNING !define MUI_ICON "..\assets\app.ico" !define MUI_UNICON "..\assets\uninstall.ico" !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "..\docs\license.txt" !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES !insertmacro MUI_LANGUAGE "SimpChinese"MUI2.nsh在 Include 目录里,找不到这个文件就是第 2 章的路径配置问题。MUI_LANGUAGE "SimpChinese"把按钮、标题、错误提示全部切到中文,主题样式也由头文件接管。这里有个细节:MUI_ICON控制安装向导页面上显示的图标,而最终 setup.exe 文件本身的图标由顶层Icon指令控制,两者不是一回事。多数项目把同一个 ico 同时给两边用,省心也少一处分歧。
4. 落地安装动作:注册表、快捷方式、静默安装的参数细节
4.1 卸载注册表条目:让系统认识你的产品
一套正经安装包都会往卸载注册表写产品信息。注意“卸载入口”不是 NSIS 自动生成的,是要在脚本里写完WriteUninstaller之后,再把卸载命令手动写进注册表。
RequestExecutionLevel admin !define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\DemoApp" Section "注册表" WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "DemoApp" WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\app.exe,0" WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "1.2.0" WriteRegStr HKLM "${UNINST_KEY}" "InstallLocation" "$INSTDIR" WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" '"$INSTDIR\uninstall.exe"' WriteRegStr HKLM "${UNINST_KEY}" "QuietUninstallString" '"$INSTDIR\uninstall.exe" /S' SectionEndHKLM需要管理员权限,所以这节开头把RequestExecutionLevel改成admin。UninstallString里我用双引号把 uninstall.exe 包住,这是防止安装路径带空格时被拆成两个参数。QuietUninstallString给控制面板“卸载”按钮走静默路径,企业批量卸载时也靠它。
卸载段要反过来清理:
Section "Uninstall" Delete "$INSTDIR\uninstall.exe" RMDir /r "$INSTDIR" DeleteRegKey HKLM "${UNINST_KEY}" SectionEnd先删 uninstall.exe 再RMDir /r删整个目录,否则卸载程序锁住自己删不掉。DeleteRegKey只删 $UNINST_KEY 这个键,千万别写成删...\Uninstall根键,那会把同机器上其他软件的卸载条目一起带走,属于无法挽回的误操作。
4.2 文件释放与快捷方式:路径别想当然
快捷方式指向的路径和文件释放路径经常错位。File /r的两种写法差异是很多人第一次翻车的地方:
| 写法 | 释放结果 |
|---|---|
File /r "payload\*.*" | payload 下内容平铺到$OUTDIR,子目录结构保留,但不会多出 payload 这一层 |
File /r "payload" | 整目录原样放进$OUTDIR,结果是$OUTDIR\payload\... |
如果安装目录里希望程序直接躺在根下,就用前者;希望建立带版本号的目录壳,就选后者。同一个脚本里两种写法混用,目录层级会叠加得乱七八糟。
快捷方式写在独立 Section 里,指向安装目录里的实际文件:
Section "快捷方式" CreateDirectory "$SMPROGRAMS\DemoApp" CreateShortcut "$DESKTOP\DemoApp.lnk" "$INSTDIR\app.exe" "" "$INSTDIR\app.exe,0" CreateShortcut "$SMPROGRAMS\DemoApp\卸载 DemoApp.lnk" "$INSTDIR\uninstall.exe" SectionEndCreateShortcut四个参数分别是快捷方式路径、目标程序、命令行参数、图标位置。空字符串表示无参数。$DESKTOP和$SMPROGRAMS是系统提供的常量,指向当前用户目录。路径里有空格时,目标路径必须整个包在双引号里,这个引号不能省,省了快捷方式栏里的目标就会被切到第一段。
File命令还要注意一个编译期的坑:makensis 解析相对路径时,是相对“当前工作目录”解析的,不是相对 .nsi 文件所在目录。也就是说在别的目录执行makensis D:\project\installer.nsi,如果脚本里写File "payload\app.exe",它会去当前工作目录找 payload,而不是去脚本目录找。解决方式是构建脚本里先执行cd /d %~dp0再调 makensis。
4.3 /S 与 /D= 静默参数:顺序是真的有讲究
企业内部推送、CI 自动化和回归验证都依赖静默安装。NSIS 的静默开关是/S,安装目录覆盖参数是/D=。这两个参数有一个反直觉的规则:/D=必须放在命令行参数的最后一位,而且即使是带空格的路径也不能加引号。
demo-setup.exe /S /D=D:\Apps\DemoApp demo-setup.exe /S /D=C:\Program Files\DemoApp第二个例子看起来会被 cmd 按空格拆开,但 NSIS 对/D=的处理是取“参数剩余部分”,所以它能容忍空格。前提是它必须是最后一个参数,后面再跟别的参数就会失效。/S的位置没有这个限制,任意位置都行。
在脚本内部,还可以用${If} ${Silent}区分静默和交互两种路径,比如静默安装时跳过快捷方式创建、不弹完成页。我一般把这类分支写在 Section 里用变量控制,而不是复制整个 Section,维护时要改的地方就少一处。
注意:
/D=只影响本次命令行运行时的安装目录,它不会反过来修改脚本里的$INSTDIR初始值。要让 UI 模式下默认目录也对,必须把InstallDir指令写在脚本里。
5. 常见问题避坑实录:编译与安装阶段的五个高频翻车现场
5.1 编译报 “Invalid command” 或 “Unknown variable”
现象:makensis 在某一行直接报错退出,错误定位到一段看起来完全正常的代码上。
原因:两种常见情况。一是用了${If}这类 LogicLib 宏,但脚本顶部没有!include "LogicLib.nsh",编译器不认识宏,把${...}当成非法命令;二是变量名写错大小写,NSIS 的$INSTDIR、$SMPROGRAMS都是固定写法,写成$InstDir就是 Unknown variable。
解决:脚本开头把所有用到的头文件一次性列齐,变量名严格用大写。编译命令统一加/V3和/WX,前者输出警告位置,后者把警告升级为错误。我的经验是:任何警告都不要拖到发布日再去查,编译机上的黄色警告就是安装现场的红色事故。
5.2 File 报 “can not be opened” 或目录层级差一层
现象:编译时 File 命令找不到源文件,或者安装完成后目录结构和预期差一个层级。
原因:一是 makensis 解析相对路径是相对当前工作目录,不是相对 .nsi 文件。换个目录执行编译命令,File "payload"就找不到东西。二是对File /r "payload\*.*"和File /r "payload"的平铺差异判断失误。三是文件被其他进程占用时,安装过程会报写入错误。
解决:构建脚本里先cd /d %~dp0再调编译命令;写 File 之前先画清最终目录层级;需要容忍占用时用SetOverwrite try让安装器尽量覆盖,失败也不至于整体中断。这套组合拳打下来,本节的翻车率能压到接近零。
5.3 杀毒误报与 SmartScreen 拦截
现象:自己机器上编译运行都正常,包发到别人机器上一跑就弹蓝色 SmartScreen 警告,或者杀毒软件隔离了部分释放文件。
原因:未签名的安装器在 Windows 10/11 上默认不受信任,SmartScreen 看到无签名的 NSIS 产物都会犹豫。部分杀毒引擎对 NSIS 生成的特征敏感,尤其再用 UPX 压过一层,误报概率直线上升。
解决:正式包务必做代码签名,命令在第 6 章。不要对成品安装包做 UPX 二次压缩,那点体积收益抵不过误报风险。内网域环境可以用组策略把签名证书加入受信任发布者,批量静默安装时非常省事。误报之后的申诉路径各家厂商都有,提交给对应厂商的误报反馈入口就好。
5.4 中文乱码与空格路径翻车
现象:安装向导里中文变成问号,或者快捷方式指向的路径被切掉半截。
原因:.nsi 源文件是 ANSI 编码,含中文时编译器按系统代码页解析出乱码;命令参数里路径没加引号,空格把字符串拦腰截断。NSIS 3.x 默认生成 Unicode 安装器,这个问题比 2.x 时代温和不少,但源文件编码不干净照样出乱子。
解决:编辑器强制把 .nsi 存成 UTF-8 with BOM;编译命令加上/INPUTCHARSET=UTF8让编译器按 UTF-8 读脚本;所有带路径的字符串一律双引号包裹。每次换编辑器或者换机器,第一件事检查源文件编码,这一条能防空大半问题。
5.5 makensis.exe 单独拷走就罢工
现象:7z 解压后只把 bin 里的 makensis.exe 复制到工具目录,编译时报“couldn’t find stubs”或者直接闪退。
原因:NSIS 的运行时是按 exe 的可执行文件位置推导的,makensis 启动后会到它自己所在目录的同级目录里找 Include、Stubs、Plugins。拆出来当孤儿文件用,自然找不到家。
解决:整个目录树一起保留,PATH 指向 bin 子目录,而不是指向 nsis 根目录。新环境配好之后,我习惯先用探针脚本验证:写一个只含 Name、OutFile、空 Section 的最小脚本,编译通过,说明关键头文件和 stub 都在位。这个探针比我拍脑袋判断“应该没问题”可靠得多。
6. 发布前的最后一公里:版本信息、图标与签名一次到位
6.1 版本信息与图标塞进同一个脚本
走进开发机看到 setup.exe 右键属性里光秃秃的,产品名、版本号全无,基本都是漏了下面这段。版本信息由VIProductVersion和VIAddVersionKey共同完成:
VIProductVersion "1.2.0.0" VIAddVersionKey "ProductName" "DemoApp" VIAddVersionKey "FileDescription" "DemoApp 安装程序" VIAddVersionKey "FileVersion" "1.2.0.0" VIAddVersionKey "LegalCopyright" "2024 Example Ltd." VIAddVersionKey "OriginalFilename" "demo-setup.exe" Icon "..\assets\app.ico" SetCompressor /SOLID lzmaVIProductVersion必须是四段数字,少一段编译直接报错。VIAddVersionKey把字段填进 exe 的属性资源里,企业资产管理软件读取的就是这些字段。Icon指令管的是安装器 exe 本身的图标,和前面 MUI_ICON 管的是页面图标,两个位置都要给到。最后加一行SetCompressor /SOLID lzma,在压缩率和编译速度之间是最平衡的选择。
6.2 签名命令与我的必走流程
代码签名是正式包的最后一道工序。在 Windows 上用 signtool:
signtool sign /fd SHA256 /f build\cert.pfx /p "证书密码" /t "证书品牌提供的RFC3161时间戳服务器地址" demo-setup.exe signtool verify /pa /v demo-setup.exe/fd SHA256指定摘要算法,现代证书链都要求它;/t是时间戳服务器地址,从证书商家后台拿,不要随便填网上的。verify 命令做完能看到签名链、时间戳和文件哈希是否一致。Linux CI 上没有 signtool 时,用 osslsigncode 也能完成同等工作。
我之前出过一次事故:脚本里漏了/WX,编译器给了一路警告照样出包,发布现场安装目录结构异常。从那以后我每次出正式包强制走三件事:先用探针脚本确认编译环境健康,再/WX编译并签名验证,最后在干净的虚拟机上做一遍静默安装完整检查。少一步都手痒。这套流程希望帮到你。
本文还有配套的精品资源,点击获取