创建安装程序






4.77/5 (28投票s)
使用 NSIS 创建简单的安装程序

引言
有许多工具可以为我们的应用程序创建安装程序。我使用了其中两个。第二个是 NSIS,自从学会它之后,我就没有再去找其他的了。本文介绍了 NSIS,并展示了如何使用强大而易用的 NSIS 脚本创建简单的安装应用程序。
![]() |
|
实验将在本文的每个部分结束后进行。在每个实验中,我们将进一步探讨该部分讨论的内容。在最后一个实验中,我们将拥有一个完整的安装应用程序。 |
目录
所需材料
在开始使用 NSIS 之前,我们需要先获取其编译器。除了 NSIS 编译器,还有许多便捷的工具和插件,大部分由 NSIS 社区编写。我在这里放了一个我在这篇文章中使用的链接。
- 下载 NSIS 安装包
- 下载 HM NISEdit
全局概览
要使用 NSIS 创建一个简单的安装程序,我们应该开始学习 NSIS 脚本语言。想法是创建一个或多个文件,其中包含脚本指令。然后,我们要求 NSIS 编译器编译该文件并创建一个安装可执行文件。
编译脚本
按照上面的链接下载并安装 NSIS 后,我们只需要创建一个名为WhatEverName.nsi 的新文本文件,输入我们的命令,右键单击该文件并选择“编译 NSIS 脚本”。

这并不算太糟,但拥有一个 IDE 会更好,不是吗?上面的第二个链接是指一个不错且小巧的 NSIS IDE,名为“HM NIS Edit”。在 NSIS 开发者中心,您可以下载其他 IDE 或集成到现有的编辑器中,如 Eclipse。这里有一个链接到 可用的文本编辑器页面。在 HM 编辑器中,我们也可以编译和运行我们的安装程序,而无需退出 IDE。我们有一个向导,一个页面设计器,并且按 F1 会启动 NSIS 脚本 CHM 帮助文件。

运行 IDE 后,您可以输入命令,或者使用向导创建一个简单安装程序的模板。就我个人而言,直到我开始从零开始创建自己的指令,我才学会使用 NSIS 脚本。
脚本详解
我们有了 NSIS,一个 IDE,并且知道我们需要编写一些指令来创建安装程序。但是我们可以输入什么呢?在 NSIS 中,每一行都被视为一个新的指令。指令将通过其名称来识别。如果我们查看一个示例 NSIS 脚本文件的指令,我们将看到其中一些部分:页面、节、函数、变量以及其他编译器命令或属性。根据它们的名称,每个指令对编译器都有特殊的含义。下面是一个包含三种所谓指令的示例脚本。

理解页面和节非常容易,因为它们代表了最终编译的安装程序中的视觉对应部分。上面的脚本,如果编译,将产生以下安装程序

上面的示例节的创建方式是隐藏的。我们也没有任何函数。稍后,我们将更详细地研究它们。
属性
属性是定义安装程序行为、外观或定义后面指令行为的指令。例如,Name
属性定义了将在安装程序窗口标题栏中显示的安装程序的名称。
Name "MyProduct Version 1.0"
下表列出了一些您可以在脚本中使用的有用属性。
属性 | 它的作用 |
名称 |
设置安装程序的名称 |
OutFile |
指定 MakeNSIS 应将安装程序写入的输出文件;安装程序文件名(如MySetup.exe) |
InstallDir |
设置默认安装目录 |
ShowInstDetails |
设置是否显示安装详情 |
ShowUnInstDetails |
设置是否显示卸载详情 |
SetCompressor |
设置用于压缩安装程序中的文件/数据所使用的压缩算法 |
SetCompressorDictSize |
设置 LZMA 压缩器使用的字典大小(以兆字节 MB 为单位) |
![]() |
|
使用 HM NIS Edit 创建一个新的 NSI 脚本文件,并在其中键入以下行: !include "LogicLib.nsh"
!include "MUI2.nsh"
!define PRODUCT_NAME "CP Lab"
!define SETUP_NAME "CPLabSetup.exe"
!define PRODUCT_VERSION "1.0"
OutFile ${SETUP_NAME}
Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
;Default installation folder
InstallDir "$PROGRAMFILES\CPLab"
;Get installation folder from registry if available InstallDirRegKey HKLM
;"Software\CP Lab" ""
ShowInstDetails show
ShowUnInstDetails show
SetCompressor /SOLID lzma
SetCompressorDictSize 12
;Request application privileges for Windows Vista RequestExecutionLevel user
;Could be 'admin'
在 NSIS 脚本中,我们可以添加宏(以 *.nsh 文件包含普通的 NSIS 脚本,就像我们的脚本一样。唯一的区别是,我们使用*.nsi 来编译我们的脚本,但要包含另一个脚本,我们必须给该文件一个*.nsh 扩展名。 请注意,在使用宏时,我们需要同时使用 |
变量
它们为我们保存值,就像我们在编程语言中所做的那样。要定义一个变量,我们需要使用var
命令。要使用它,我们必须在变量名前面加上$
。
var varName
...
StrCpy $varName "example value"
使用变量时的注意事项
- 所有变量都是全局的,因此在函数内部定义时,我们必须显式使用
/GLOBAL
开关:Var /GLOBAL varName
- 变量名允许的字符是:[
a
-z
]、[A
-Z
]、[0
-9
] 和_
- 默认情况下,变量限制为 1024 个字符。
预定义变量
- NSIS 中已经定义了一些变量,我们可以直接使用它们而无需声明。此外,我们必须小心并避免我们的名称与这些名称发生冲突:
$0
、$1
、$2
、$3
、$4
、$5
、$6
、$7
、$8
、$9
、$R0
、$R1
、$R2
、$R3
、$R4
、$R5
、$R6
、$R7
、$R8
、$R9
。NSIS 有时将这些变量称为寄存器。 $INSTDIR
:安装目录。可以在脚本的任何地方使用,并在运行时替换为安装目录路径。当您使用InstallDir
属性时,此变量已由您设置,但在运行时用户可以更改它。您也可以使用StrCpy
命令修改此变量。$OUTDIR
:当前输出目录。可以使用SetOutPath
或StrCpy
来设置此变量。
有关变量的更多信息,请参阅 NSIS 帮助文件中的变量部分。
常量
这里是命名一些 NSIS 为我们定义的有用常量的好地方。它们也已添加到帮助文件的变量部分。还记得InstallDir
属性吗?如果您不记得了,它是设置安装目录的属性。除非您确定希望将安装程序文件准确地复制到哪里(例如C:\MyFolder),否则您需要知道一些路径,例如Windows文件夹路径或Program Files文件夹路径。常量在这里很有帮助。在运行时,它们将被替换为最终用户计算机的路径。请看下面的例子。
InstallDir "$PROGRAMFILES\MyApp"
在这里,我们在InstallDir
属性前面使用了$PROGRAMFILES
常量,该属性期望一个字符串。$PROGRAMFILES
将在用户计算机上被替换为确切的路径字符串。下表显示了一些有用的常量列表。
恒定 | 含义 |
$PROGRAMFILES |
通常是C:\Program Files,但在不同机器上可能不同。 |
$PROGRAMFILES32 、$PROGRAMFILES64 |
在 Windows X64 上,前者指向C:\Program Files (x86),后者指向C:\Program Files。 |
$DESKTOP |
Windows 桌面目录(通常是C:\Windows\Desktop,但会在运行时检测)。 |
$EXEDIR |
包含安装程序可执行文件的目录。 |
$EXEPATH |
安装程序可执行文件的完整路径。 |
${NSISDIR} |
一个包含 NSIS 安装路径的符号。如果您想调用 NSIS 目录中的资源,例如图标、UI,则很有用。 |
$WINDIR |
Windows 目录。 |
$SYSDIR |
Windows 系统目录。 |
$TEMP |
系统临时目录。 |
$STARTMENU |
开始菜单文件夹(使用CreateShortCut 添加开始菜单项时很有用)。 |
$SMSTARTUP |
开始菜单程序/启动文件夹。此常量的上下文(所有用户或当前用户)取决于SetShellVarContext 设置。默认是当前用户。 |
$DOCUMENTS |
文档目录。此常量的上下文(所有用户或当前用户)取决于SetShellVarContext 设置。默认是当前用户。 |
$APPDATA |
应用程序数据目录。此常量的上下文(所有用户或当前用户)取决于SetShellVarContext 设置。默认是当前用户。 |
$CDBURN_AREA |
存储待刻录到 CD 的文件的目录;此常量在 Windows XP 及以上版本可用。 |
有关常量的完整列表或其要求,请参阅 NSIS 帮助的变量部分。
节
“每个 NSIS 安装程序包含一个或多个节。”这意味着我们的脚本中至少需要有一个节。但是节有什么用呢?您肯定见过许多安装程序允许您选择要安装的内容,例如非常著名的 Microsoft Office,它允许您选择安装 Powerpoint 或不安装。
在 NSIS 中,可以通过节将这些选项提供给用户。在我们的示例中,Microsoft Powerpoint 应该有一个特定的节,而该节实际上为每个您选择安装的功能都有其他节。至少有一个节是合乎逻辑的,因为我们至少有一个要安装的功能。不是吗?节可以隐藏,这样用户就看不到任何供其选择的功能,这很可能用于只有一个节的安装程序。
Section "Installer"
; Instructions go here
SectionEnd
在上面的示例中,我创建了一个名为“Installer”的新节。不要被名称误导;它只是一个名称,您可以将其命名为 MySec。我们可以在节块内自由放置任意数量的指令。我在这里不详细描述节;这会使本文过长。但是,您可以找到许多关于节的信息,例如如何禁用或重新启用一个节,如何创建节组,如何使节名称显示为粗体,使其成为可选或强制的等等。
![]() |
|
将上一个文件重新加载到您的编辑器中,然后键入以下内容: Section "Dummy Section" SecDummy
SetOutPath "$INSTDIR"
;Store installation folder
WriteRegStr HKCU "Software\CP Lab" "" $INSTDIR
;Create uninstaller
WriteUninstaller "$INSTDIR\Uninstall.exe"
SectionEnd
Section "Uninstall"
Delete "$INSTDIR\Uninstall.exe"
RMDir "$INSTDIR"
DeleteRegKey /ifempty HKCU "Software\CP Lab"
SectionEnd
上面的代码创建了两个节。我们希望第一个可见。上面的第二个节将在卸载时调用。NSIS 编译器通过名称识别这一点。如果节的名称是 UnInstall 或以 un 开头,它将在卸载时调用。 警告:请注意,调用 |
函数
“函数类似于节,因为它们包含零个或多个指令。”函数有两种类型:用户函数和回调函数。用户函数将通过使用Call
指令手动调用。“回调函数将在某个事件发生时由安装程序调用。”
“函数必须在节或其他函数之外声明。”
Function func
; some commands
FunctionEnd
Section
Call func
SectionEnd
上面的代码展示了如何创建一个用户函数。还有另一种类型的函数称为“回调”。回调函数通过它们的名称来识别。这些名称是唯一的,NSIS 编译器可以识别它们。这些函数将在特殊事件发生时被调用,您可以自由地在其中放置您喜欢的指令,以便在事件发生时执行它们。
Function .onInit
MessageBox MB_YESNO "This will install. Continue?" IDYES NoAbort
Abort ; causes installer to quit.
NoAbort:
FunctionEnd
上面的代码展示了如何编写.onInit
,以便一旦安装程序启动,一个消息框就会询问用户是否要继续。下表列出了一些常见的回调函数。
回调函数名 | 调用时间 |
.onInit |
将在安装程序几乎完成初始化时调用。如果.onInit 函数调用Abort ,安装程序将立即退出。 |
.onUserAbort |
当用户按下“取消”按钮且安装尚未失败时调用。如果此函数调用Abort ,则安装不会被中止。 |
.onInstFailed |
在安装失败后用户按下“取消”按钮时调用。 |
.onMouseOverSection |
每当鼠标在节树上的位置发生变化时调用。这允许您为每个节设置描述,例如。当前鼠标悬停的节 ID 暂时存储在$0 中。 |
un.onInit |
将在卸载程序几乎完成初始化时调用。如果un.onInit 函数调用Abort ,卸载程序将立即退出。 |
有关函数的更多信息,请参阅 NSIS 帮助中的函数部分。
![]() |
|
让我们为我们的安装程序添加两个函数:一个回调函数和一个用户函数。 Function .OnInit
StrCpy $0 "Welcome to my first setup wizard"
push $0
Call ShowWelcome
FunctionEnd
Function ShowWelcome
pop $R0
${If} $R0 == ''
StrCpy $R0 "Message from function"
${EndIf}
MessageBox MB_OK $R0
FunctionEnd
这里我们创建了一个回调函数 用户函数尝试从堆栈中弹出一个字符串到 这展示了我们如何创建函数、传递参数(使用内置堆栈)、使用变量以及使用我们已经包含的 |
页数
“每个(非静默)NSIS 安装程序都有一组页面。每个页面可以是 NSIS 内置页面,也可以是自定义页面。”在我写这篇文章的时候,NSIS 有了一个新版本,它提供了更好的自定义页面支持,但我懒得去读。我将这项任务留给热情的读者。Page 指令很简单。
Page license
上面的代码显示了一个简单的空许可证页面。要强制显示您的许可证文件,您应该使用页面选项。事实上,不仅是许可证页面,所有其他默认页面也都有可以通过属性提供的选项。另一种编码 UI 的方法是使用现代 UI。它不仅更简单,而且更熟悉、更美观。Modern UI 在 NSIS 2.0 版本之后包含。UI 的文档可在此处找到。在我撰写本文时,Modern UI 的第二版已经可用,并具有更强大的功能。
!insertmacro MUI_PAGE_LICENSE "License.rtf"
上面的代码添加了一个许可证页面,并加载指定的文件供用户签名。页面中最后一个重要的东西是它们都有事件(还记得回调吗?)。有了这些回调处理程序,您可以轻松修改它们的行为方式,或决定在页面显示之前或之后做什么。例如,当我们要忽略下一页或根据前几页启用/禁用某些选项时,这很有用。
!define MUI_PAGE_CUSTOMFUNCTION_PRE PAGE_LICENSE
!insertmacro MUI_PAGE_LICENSE "License.rtf" "" PreLicense
...
Function PreLicense
; If app installed already, license signed, ignore it
${If} $bAppExists == '1'
Abort
;${Else}
${EndIf}
FunctionEnd
上面的代码在显示许可证页面之前调用一个函数。该函数检查一个变量以查看应用程序是否已存在。如果存在,将忽略许可证页面,并显示下一页。
![]() |
|
如果我们现在运行我们的安装程序,它将起作用!它使用节、我们的属性以及一些默认行为来执行所需的操作。问题是,用户没有任何自定义选项。让我们通过现代 UI 的页面给用户一些选择。 !insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "License.txt"
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
!insertmacro MUI_LANGUAGE"English"
;--------------------------------
;Descriptions
;Language strings
LangString DESC_SecDummy ${LANG_ENGLISH} "A section"
;Assign language strings to sections
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SecDummy} $(DESC_SecDummy)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
只需添加一些页面,我们就完成了。在完成本次实验之前,请注意我们如何使用 |
实用说明
下表列出了一些您可能会需要的指令。
指令 | 它的作用 | 用法 |
文件 |
将文件添加到当前输出路径($OUTDIR )进行解压 |
File "Bin\7z\*.*" |
退出 |
导致安装程序尽快退出 | |
ExecWait |
执行指定的程序并等待已执行进程退出 | ExecWait 'c:\SomeProgram.exe' $0 |
DetailPrint |
将字符串“message”添加到安装程序的详细信息视图中 | DetailPrint "message" |
Strlen |
用 str 的长度设置用户变量 $x |
Strlen $0 ${SETUP_NAME} |
ReadRegStr |
从注册表中读取到用户变量 | ReadRegStr $0 HKLM Software\NSIS "" |
CopyFiles |
将文件从源复制到目标安装系统 | CopyFiles "$0\cid.dll" "$INSTDIR" |
CreateDirectory |
创建(必要时递归创建)指定目录 | CreateDirectory "7z" |
CreateShortCut |
创建一个快捷方式 link.lnk ,链接到 target.file ,并带有可选参数 parameters |
CreateShortCut "$SMPROGRAMS\$STMenuDirectory\Help.lnk" "$INSTDIR\Yas.chm" |
IfFileExists |
检查文件是否存在 | IfFileExists "$INSTDIR\${SQL_DATABASE_NAME}.mdf" 0 Goon_label |
删除 |
从目标系统删除文件(可以是文件或通配符,但应指定完整路径) | Delete '"$INSTDIR\${SQL_DATABASE_NAME}.mdf"' |
DeleteRegKey |
删除注册表项 | DeleteRegKey[/ifempty] root_key subkey |
DeleteRegValue |
删除注册表值;root_key 的有效值列在 WriteRegStr 下 |
DeleteRegValue root_key subkey key_name |
最后说明
请注意,本文包含的安装程序将在您的Program Files目录下创建一个名为CPLab的文件夹。文件夹内有一个Uninstall.exe,它将为您清理所有内容。这里还有很多特性或需要提示的内容需要提及,但如果我那样做,我就会把 NSIS 帮助重新排序。我认为这足以开始创建您的安装程序,并且正如我创建自己的安装程序时所做的那样,您一定会很快找到所需的内容。祝您好运!
致谢与免责声明
这里的大部分文本和代码都摘自 NSIS 帮助和示例。我试图在文本周围加上引号,但对于代码,这反而会显得杂乱。感谢 NSIS 社区,这里有大量可供下载的代码 这里。
历史
2008/03/04:教程创建