NSIS(Nullsoft Scriptable Install System)是一个开源的 Windows 平台安装包制作程序。NSIS 通过它定义的脚本语言来描述安装包的行为和逻辑的。

一. 环境搭建

http://nsis.sourceforge.net/Download下载 NSIS 安装进行安装。

使用 NSIS 生成安装包的大致流程如下:

  1. 使用任意文本编辑器(如 Notepad++、Visual Studio Code)开发 NSIS 脚本
  2. 使用安装目录内的 makensisw.exe 程序编译 NSIS 脚本生成安装包。

makensisw.exe 只提供编译脚本的功能,不能对脚本进行调试,调试脚本全靠错误提示和经验。

二. NSIS 脚本结构

一个 NSIS 脚本可以包括:安装程序属性、页面、区段等。

对于一个最简单的 NSIS 脚本,只有 OutFile属性和区段(一个或多个)是必须的,OutFile 属性用于指定 NSIS 编译生成安装包的路径。

1
2
3
4
OutFile "Simple.exe"

Section "Installer Section"
SectionEnd

保存上面脚本到任意 .nsi 文件,并使用 makensisw.exe 打开改脚本进行编译,出现下图表示编译成功,成功之后会在脚本当前目录看到输出的 Simple.exe 文件。

如果编译失败,makensisw.exe 会在输出窗口提示失败原因。

需要注意:
如果 NSIS 脚本内包含中文等非 ASCII 字符,需要将脚本保存为 ANSI 编码格式,而不能保存为 UTF8 格式。

2.1 安装程序属性

安装程序的属性用于控制安装程序的外观,比如指定哪些页面出现在安装程序里,在每个页面的每个部分显示什么文本,安装程序的名称(如上面脚本中的 OutFile),使用什么样的图标,默认安装目录等等。

安装程序属性可以在除了区段和函数以外的任何地方设置。

可以理解为安装程序属性是设置给 NSIS 编译器看的,这些属性的值在编译时都已经确定了,所以除了 InstallDir 以外,其他安装程序属性都不允许使用变量(除了$\r$\n)。

2.2 完整的安装程序属性

NSIS教程(2)-完整属性

2.2 页面

页面分为NSIS内置的向导页面用户自定义页面

一个非静默安装程序需要页面来指导用户运行安装程序,可以通过Page命令或PageEx(PageEx 提供了更多高级的设置)命令来设定哪个页面显示。

页面实际的显示顺序和它在 NSIS 脚本中定义的顺序是一致的。

2.2.1 内置向导页面:

NSIS 内置的向导页面有:

  • license:许可证页面。
  • components: 组件选择页面,每个可见的区段都可以作为一个组件给用户选择是否安装。你可以只使用一个区段来构建安装包,但是如果你想要使用组件页来让用户选择可选的组件,那你就需要使用多个区段。
  • directory: 安装目录选择页面。
  • instfiles: 安装过程页面。
  • uninstConfirm: 卸载确认页面。

不同的页面,有不同的属性。

内置页面语法如下:

1
Page (license|components|directory|instfiles|uninstConfirm) [预置函数] [显示函数] [离开函数]

每个内建的页面都有三个回调函数(预置函数、显示创建函数和离开函数),预置函数在页面被创建之前被直接的调用,显示函数在页面被创建后且在显示之前被直接调用,离开函数在用户按下下一页按钮之后并且在页面离开之前被直接调用。

如:

1
2
3
PageEx license
LicenseData "license.rtf" #可以是txt或rtf文件格式
PageExEnd

2.2.2 自定义页面

创建自定义页面语法:

1
page custom [创建函数] [离开函数] [标题]

自定义页面只有两个回调函数(创建函数和离开函数),创建函数在需要创建页面时被调用,离开函数在用户按下”下一页”按钮之后并且在页面离开之前被直接调用。

2.2.3 页面回调函数

每个内建的页面都有三个回调函数:一个预置函数、一个显示创建函数和一个离开函数。

预置函数在页面被创建之前被直接的调用,显示函数在页面被创建后且在显示之前被直接调用,离开函数在用户按下下一页按钮之后并且在页面离开之前被直接调用。

  • 预置函数允许使用 Abort 来略过该页面。
  • 显示函数允许使用 CreateFont、SetCtlColors、SendMessage 和其他来调整页面的用户界面。
  • 离开函数允许使用 Abort 来强制用户停留在当前页面。

一个自定义的页面仅有两个回调函数,一个是必须的创建页面,另一个离开函数的作用就和内建页面的离开函数一样。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Page license skipLicense "" stayInLicense
Page custom customPage "" ": 自定义页面"
Page instfiles

Function skipLicense
MessageBox MB_YESNO "你想略过许可页面吗?" IDNO no
Abort
no:
FunctionEnd

Function stayInLicense
MessageBox MB_YESNO "你想停留在许可页面吗?" IDNO no
Abort
no:
FunctionEnd

Function customPage
GetTempFileName $R0
File /oname=$R0 customPage.ini
InstallOptions::dialog $R0
Pop $R1
StrCmp $R1 "cancel" done
StrCmp $R1 "back" done
StrCmp $R1 "success" done
error: MessageBox MB_OK|MB_ICONSTOP "InstallOptions error:$\r$\n$R1"
done:
FunctionEnd

2.3 区段(Section)

在安装程序中用户需要安装许多东西,如安装源码、附加插件、脚本样例或其他。里面的每个安装组件都有它自己的代码块,当用户选择了安装该组件,那么安装程序就会执行对应的代码。

在 NSIS 脚本里,这些代码称为区段。每个可见的区段都可以作为一个组件给用户选择是否安装(区段也可以设置属性对用户隐藏,默认安装,让用户无法选择是否安装)。

NSIS 脚本对区段的数量没有限制,你可以只使用一个区段来构建安装包,但是如果你想要使用组件页来让用户选择可选的组件,那你就需要使用多个区段了。

卸载程序也可以有多个区段,但卸载程序区段名前要加上前缀un.,因为卸载程序是要被编译成是一个单独的 exe 的,所以要对编译器指明哪些内容(如区段)需要被编译进卸载程序,因此需要特殊前缀。

区段名为空、遗漏或者以一个 “-“ 开头,那么它将是一个隐藏的区段,用户也不能在组件选择页面选择是否禁止。

三. NSIS 语法

3.1 语法概述

  • 单行注释用井号#或分号;,跨行注释用可以用 C/C++中注释语法;
  • 数字常量可以用十进制、十六进制(0x 为前缀)、八进制(0 为前缀)表示,颜色用类似 html 的中 RGB 表示法,但去井号”#”。
  • 字符串常量可以用引号引用,特殊的字符可以使用美元符号”$”作前缀来转义。美元符号、换行、回车、制表符都是特殊字符,需要转义,他们在NSIS中可以写成: $$, $\n, $r, $\t
  • NSIS 脚本用行尾的反斜杠”"表示下一行和当前行逻辑上是同一行。

3.2 常量和变量

NSIS 的变量和常量都是全局的,且大小写敏感的,NSIS 中变量和常量都是弱类型的。

常用和变量的引用方式都是${VAR_NAME}$VAR_NAME

使用!define方式定义常量,如:!define PRODUCT_NAME "网易云音乐"

使用Var方式定义变量,如:Var a

NSIS 内置了 20 个已注册的变量:$0 ~ $9$R0 ~ $R9,这些变量不需要声明就可以使用,一般用于参数传递等。

另外,NSIS 内置一个堆栈,使用pushpop命令来入栈和出栈,可以使用堆栈来暂存数据。

关于变量的赋值,NSIS 中不能直接使用=来赋值,需要借助StrCpy来实现,如:

1
2
StrCpy $0 "hello"
StrCpy $0 123

3.3 函数

3.3.1 函数定义

1
2
3
Function 函数名

FunctionEnd

函数名以“.”开头的(例如 “.Whatever”)一般作为回调函数保留。
函数名以“un.”开头的函数将会被创建在卸载程序里。因此,普通安装区段和函数不能调用卸载函数,而卸载区段和卸载函数也不能调用普通函数。
NSIS 函数声明中不支持参数定义,如果需要传递参数,可以使用内置的 20 个变量或者堆栈的方式。

3.3.2 函数调用

1
Call 函数名

3.4 宏

除了函数之外,NSIS 还支持定义,NSIS 中的宏支持定义参数。

3.4.1 宏定义

1
2
3
!macro 宏名 [参数1] [参数n]

!macroend

3.4.2 宏调用

1
!insertmacro 宏名 [参数1] [参数n]

3.5 指令

NSIS 内置了很多指令,这些指令提供了安装包常用了功能,如果这些指令无法满足需求,用户也可以自己开发插件。

NSIS 支持的指令列表见:NSIS教程(3)-完整指令

NSIS 指令使用中比较特别的在于跳转,比如执行失败跳转到哪里,成功跳转到哪里。
MessageBox指令为例([]为可选项):

1
MessageBox 消息框选项列表 消息框文本 [/SD 返回] [检测返回值 跳转到] [检测返回值2 跳转到2]

下面的用法表示如果 MessageBox 返回 IDYES,则+2,即跳转到该指令的下 2 条执行(+1 该指令的下 1 条,+2 就为该指令的下 2 条),/SD IDYES表示安装包采用静默安装方式时默认用户选择了IDYES:

1
MessageBox MB_ICONQUESTION|MB_YESNO "你确实要完全删除网易云音乐,及其所有组件吗?" /SD IDYES IDYES +2 IDNO +1

四. NSIS 界面

NSIS 提供的界面分为传统界面(Classic UI)、现代界面(Modern UI)。

4.1 传统界面

4.2 现代界面

4.3 自定义界面

我们可以使用第三方界面库(如 duilib, Qt)自己绘制安装包的界面,这样灵活度更大,可以开发出类似 QQ 那样的安装界面。
在此之前需要掌握 NSIS 的插件开发技巧。