Function .onInit MessageBox MB_YESNO "This will install My Program. Do you wish to continue?" IDYES gogogo Abort gogogo: FunctionEnd
Abort函数在回调函数中有特殊的含义,每一个回调函数中都有不同的含义,具体要参考回调函数的说明。
脚本的使用
代码逻辑:
顺序、条件、循环是现代编程语言的三个基本的结构,在该脚本中,你可以使用StrCmp、IntCmp、IfErrors、Goto等来对应这些结构。但是有更好的方法来实现,就是使用LogicLib。作为对比:
StrCmp $0 'some value' 0 +3 MessageBox MB_OK '$$0 is some value' Goto done StrCmp $0 'some other value' 0 +3 MessageBox MB_OK '$$0 is some other value' Goto done # else MessageBox MB_OK '$$0 is "$0"' done:
上面这段代码是未使用LogicLib的代码,再看看使用LogicLib的代码:
${If} $0 == 'some value' MessageBox MB_OK '$$0 is some value' ${ElseIf} $0 == 'some other value' MessageBox MB_OK '$$0 is some other value' ${Else} MessageBox MB_OK '$$0 is "$0"' ${EndIf}
相比之下,后者就显得结构清晰、简洁。
要使用LogicLib只需要在脚本前添加引用:
!include LogicLib.nsh
变量:
你可以使用Var命令来定义你的变量,这个变量是全局的,你可以在任何段和函数中使用。
Var BLA ;定义一个变量BLA Section bla StrCpy $BLA "123" ;这里你就可以使用BLA变量,这里要在BLA前面加上$前缀。 SectionEnd
另外还有一个变量堆栈,你可以使用push、pop命令来操作这些变量。
对于共享模式的代码,一共有20个注册变量($0-$9, $R0- $R9),这些变量不需要申明,不过你最好在使用之前保存初值,使用完之后恢复初值。还有一点就是这些变量的使用要按照堆栈的方式,first-in-last-out。
Function bla Push $R0 Push $R1 ...code... Pop $R1 Pop $R0 FunctionEnd
调试脚本:
调试程序最好的办法就是打印输出。NSIS脚本的调试也是使用打印的方法:
MessageBoxs和DetailPrint。要想得到所偶变量的值可以使用插件DumpState。
默认情况下,所有的操作都会卸载Windows的日志中,你可以在windows日志查看。
脚本的执行:
当用户运行安装或者卸载程序的时候(似乎就这两种情况了),页Page会按照它们在脚本文件中的顺序出现。除了段中的代码,还有回调函数也可能在段的代码执行之前先执行,比如说.onInit函数将会在安装程序所有页面出现之前先被执行。还有页Page的回调函数。
编译命令:
编译命令在你的计算机上编译脚本的时候执行。这个可以被用来做条件编译、包含头文件、执行应用程序、改变工作目录等。最常用的就是define定义,define是编译期常量,你可以定义你的产品版本,并在脚本中使用。例如:
!define VERSION "1.0.3" Name "My Program ${VERSION}" OutFile "My Program Installer - ${VERSION}.exe"
另一个用法是宏macro。使用宏定义对于一般程序员来说应该不陌生,它可以简化代码的编写,其实意义上类似于函数。宏也是在编译器替换、插入、执行的。比如下面的定义:
!macro MyFunc UN Function ${UN}MyFunc Call ${UN}DoRegStuff ReadRegStr $0 HKLM Software\MyProgram key DetailPrint $0 FunctionEnd !macroend !insertmacro MyFunc "" !insertmacro MyFunc "un."
宏可以帮助你避免写重复的代码,上面这两个!insertmacro语句插入了两个函数,一个用来安装程序时使用MyFunc,一个在卸载程序时使用un.MyFunc。两个函数是相同的。
编译
写好脚本之后就是编译脚本了,makeNSIS.exe就是NSIS的编译器,它吃进脚本吐出安装程序。当然你可以使用集成的编译环境比如说HM NIS Edt,编译的时候会检查脚本的语法等,并给出警告和错误提示。也可以在Linux、BSD或者Mac OS X上编译Windows程序,具体看NSIS的用户手册。
界面
Modern UI不仅是一个自定义资源文件,而且还提供了大量的接口元素。
插件
其实就是dll文件,这些文件一般用C/C++,Delphi或者其他语言编写,作为NSIS的一个强大的扩展。
引入插件的语句:
DLLName::FunctionName "parameter number 1" "parameter number 2" "parameter number 3"
每一个插件根据不同的参数都有不同的要求,有些不需要参数,有些你可以给出很多参数:
nsExec::ExecToLog '"${NSISDIR}\makensis.exe" /CMDHELP' InstallOptions::dialog "$PLUGINSDIR\test.ini" NSISdl::download http://download.nullsoft.com/winamp/client/winamp291_lite.exe $R0
在NSIS的安装目录下有一个插件目录,NSIS会默认搜索这个目录、并列出可用的函数,当然你也可以使用!addplugindir命令来引入新的插件目录。
NSIS的发布版已经自带了一些插件,当然你也可以自己写插件,NSIS手册上有介绍。
2. 命令行用法
MakeNSIS用法
makensis [option | script.nsi | - [...]]
基本上现在我没有用到命令行,现在直接使用集成环境。
安装、卸载用法
可以设置静默安装、卸载。设置安装在默认的安装目录等。
3. 脚本说明
脚本文件格式
脚本文件只是一个写了脚本语句的文本文件。
命令
command [参数...]
比如:File "MyFile"
注释
以#或者;开头语句就是注释语句,也可以使用C风格的注释。比如:
; Comment # Comment # Comment \ Another comment line (see `Long commands` section below) /* Comment Comment */ Name /* comment */ mysetup File "myfile" ; Comment
插件
plugin::command [参数...] 比如:
nsExec::Exec "MyFile"
数字
NSIS脚本支持三种进制的数字,八进制,十进制,十六进制。
颜色必须使用十六进制。例如:
IntCmp 1 0x1 lbl_equal SetCtlColors $HWND CCCCCC
字符串
如果字符串中有空白字符,要使用双引号包起来:
MessageBox MB_OK "Hi there!"
一共可以使用单引号、双引号、反单引号三种,并且可以嵌套。你也可以使用$\符号来跳出引号。
MessageBox MB_OK "I'll be happy" ; this one puts a ' inside a string 打印:I'll be happy
MessageBox MB_OK 'And he said to me "Hi there!"' ; this one puts a " inside a string 打印:And he said to me "Hi there!"
MessageBox MB_OK `And he said to me "I'll be happy!"` ; this one puts both ' and "s inside a string 打印: And he said to me "I'll be happy!"
MessageBox MB_OK "$\"A quote from a wise man$\" said the wise man" ; this one shows escaping of quotes 打印:"A quote from a wise man" said the wise man
变量
变量以$开头,定义及用法例如:
Var MYVAR StrCpy $MYVAR "myvalue"
长命令
类似于C类语言,可以使用反斜杠(\)来换行继续上一行的语句。这个对于命令和注释都适用。
CreateShortCut "$SMPROGRAMS\NSIS\ZIP2EXE project workspace.lnk" \ "$INSTDIR\source\zip2exe\zip2exe.dsw"
# A comment \ still a comment here...
配置文件
如果在配置目录下存在一个"nsisconf.nsh"文件,默认它将会在所有脚本之前包含进来(除非/NOCONFIG),Windows平台上这个配置目录就是makensis.exe所在目录。其他平台下这个值在安装时期设置并且默认值是$PREFIX/etc/。
变量
用户自定义变量
Var命令,可以使用Var命令定义自定义的变量,这样定义出来的变量是全局的,且不论你是在段中还是在函数中定义。
但是如果在段或者是函数中定义变量要加上/GLOBAL前缀:
Var example Function testVar Var /GLOBAL example2 ;看这里的定义 StrCpy $example "example value" StrCpy $example2 "another example value" FunctionEnd
其他可写的变量
$0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $R0, $R1, $R2, $R3, $R4, $R5, $R6, $R7, $R8, $R9
这些变量可以像自定义变量那样使用,不过这些变量一般在共享函数和宏里面使用。正如篇前所说,最好在使用的过程中记录和重新装载这些变量的初值。这些变量也可以用来和插件通信,因为插件也可以使用这些变量。
$INSTDIR
安装目录,可以用StrCpy、ReadRegStr、ReadINIStr来修改。比如.onInit函数需要做一个更高级的安装检测。
需要注意的是在卸载代码中,$INSTDIR所表示的目录正是卸载文件所在目录。我们并没有必要保证这个值要和安装程序中的一致,比如说,你把卸载文件写在了#WINDIR中,并且用户没有移动它,那么写在文件中的$INSTDIR就是$WINDIR。如果你把卸载文件放在了其他位置,那么你最好把这个路径记录下来,并在卸载的时候可以读取,以便卸载程序。
$OUTDIR
当前的输出目录,使用SetOutPath,StrCpy、ReadRegStr、ReadINIStr等修改。
$CMDLINE
安装程序的命令行。
$LANGUAGE
标识当前使用的语言的ID,例如English是1033,你可以在.onInit中修改它。
常量
常量也可以用在安装属性设置中。
需要注意的是,并非所有常量都可以运行在所有操作系统上,比如$CDBURN只能工作在Windows XP及以上版本中。
$PROGRAMFILES, $PROGRAMFILES32, $PROGRMAFILES64
默认安装目录是C:\Program Files,但是这个会在运行时检测,在Windows x64平台上,$PROGRAMFILES、$PROGRAMFILES32表示C:\Program Files (x86),$PROGRAMFILES64表示C:\Program Files。
还有很多这样的常量,这里就不一一列举了,可以查看NSIS用户手册。这些变量就是提供了设置系统中一些参数的简单方式。
字符串中的常量
$$:表示$
$\r:表示\r
$\n:表示\n
$\t:表示\t
标签
标签用于Goto命令、或者是一些分支结构(IfErrors, MessageBox, IfFileExists, StrCmp)。标签必须位于段或者函数内部。并且标签是局部的,也就是段或者函数的内部才可以访问。定义方法比如:
MyLabel:
标签的命名不能以 -、+、!、$、或者0-9开头,使用标签的时候要注意空字符串("")和0都表示下一个指令的意思,以一个点号(.)开头的标签表示拥有全局作用域,意思是你可以在任何函数或者段中跳到这个标签。但是你不能从安装代码跳到卸载代码,反之亦然。
相对跳转
你可以在可以使用标签的地方使用相对跳转。用法是如下:+1表示跳转至吓一跳指令;+2表示忽略下一条指令、直接跳转到下下一条命令;-2表示往回跳两条指令,+10表示忽略后面的9跳指令、直接跳至第10条指令。
指令就是安装程序运行时的所有命令,MessageBox、Goto、GetDllVersion、FileRead、SetShellVarContent都是指令。AddSize、Section、SectionGroup、SectionEnd、SetOverWrite,Name、SetFont、LangString、这些都不是指令、因为它们在编译时执行。
Goto +2 MessageBox MB_OK "You will never ever see this message box" MessageBox MB_OK "The last message was skipped, this one should be shown"Goto +4 MessageBox MB_OK "The following message will be skipped" Goto +3 MessageBox MB_OK "You will never ever see this message box" Goto -3 MessageBox MB_OK "Done"注意如果想对跳转后面有插入宏,那么宏不会被视为一条指令,因为宏在跳转执行之前会被展开。这一点上宏的使用类似C类语言。比如:
!macro relative_jump_test MessageBox MB_OK "first macro line" MessageBox MB_OK "second macro line" !macroend Goto +2 !insertmacro relative_jump_testGoto将会跳转到"second macro ine"。
页 Pages
每一个非静默安装的NSIS安装程序,都有一组安装页。这个页可以是NSIS内置的页,也可以是用户的函数创建的自定义页。
使用脚本你可以控制页的顺序、显示和行为,你可以跳过页、把他们画成白板、强制用户停留在某个页面直到满足一定的条件,显示一个Read me页、显示自定义的输入页面等等。
这里有两个和页相关的命令,Page和UninstPage。第一个命令添加一个页到安装程序,第二个添加一个页到卸载程序。还有一个命令PageEx,他允许你添加一个页,并附加一些选项。这个命令可以让你把指定的页替换掉默认的页。
顺序
页的顺序就是Page、UninstPage、PageEx在脚本中出现的顺序,比如说:
Page license Page components Page directory Page instfiles UninstPage uninstConfirm UninstPage instfiles上面的代码,将会首先显示一个许可证页、接着是组件页、接着是选择安装目录页、接着是执行的段的安装日志页。卸载程序将会首先显示一个卸载确认页、接着是卸载日志页。
为了向后兼容,如果没有写任何安装程序的Page命令脚本的话,以下页面将会自动添加:license许可证页(如果指定LicenceText和LicenceData的话),components组件页(如果ComponentText指定了并且有超过一个可视段),directory目录页(如果DirText指定了的话)和安装日志页。如果没有卸载页命令的话,下面的卸载页将会被添加:卸载确认页和卸载日志页。这些方法现在已经被弃用,推荐使用页。
页选项
每一个页都有一组数据定义了页长什么样子,有什么动作。下面描述了每一种页使用那些数据以及如何设置。回调函数不会再本段描述。
下面这个列表列出了命令如何影响具体的页面类型,除非特别说明,下面的命令可被用于PageEx块的内部和外部,如果用在PageEx块内部,它们仅仅会影响用PageEx设置的当前页,否则它们会影响所有页面的默认行为。
License page
- LicenseText
- LicenseData
- LicenseForceSelection
Components selection page
- ComponentText
Directory selection page
- DirText
- DirVar - can only be used in PageEx
- DirVerify
Un/Installation log page
- DetailsButtonText
- CompletedText
Uninstall confirmation page
- DirVar - can only be used in PageEx
- UninstallText
回调函数
每个内置页都有三个回调函数:pre-function、show-creation和leave-function。pre-function在页创建之前调用,show-creation正好在页创建之后且在页显示之前调用,leave-funtion在用户点击下下一步按钮之后并还停留在当页的时候调用。
pre-function允许你使用Abort跳过页
show-function允许你使用CreateFont、SetCtlColors、SendMessage和其他函数修改用户界面
leave-function允许你使用Abort函数强制用户停留在当页
一个自定义页仅有两个回调函数,一个是强制性的创建页函数,一个是leave-function,就像内置页的回调函数一样。例子:
Page license skipLicense "" stayInLicense Page custom customPage "" ": custom page" Page instfiles Function skipLicense MessageBox MB_YESNO "Do you want to skip the license page?" IDNO no Abort no: FunctionEnd Function stayInLicense MessageBox MB_YESNO "Do you want to stay in the license page?" 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
安装卸载 页 [Uninst]Page
custom [creator_function] [leave_function] [caption] [/ENABLECANCEL] OR internal_page_type [pre_function] [show_function] [leave_function] [/ENABLECANCEL]
添加一个安装/卸载页,其中internal_page_type可以是:
license - 许可证页
componenets - 组件选择页
directory - 目录选择页
instfiles - 执行段中的安装页
uninstConfirm - 卸载的确认页
安装的最后一页,有一个取消按钮用来阻止确认。如果要这样的话,那么就要使用/ENABLECALCEL
扩展页 PageEx
[un.](custom|uninstConfirm|license|components|directory|instfiles)
添加一个安装页,或者卸载页(如果使用un.前缀),每一个PageEx都必须有一个PageExEnd与之对应。在这个页中你可以设置关于这个页(不影响其他页)的选项,如果这些选项没有设置,那么选项会使用外部PageEx的设置,如果什么都没有,那么会使用默认值。设置二级标题的话使用Caption或者SubCaption来设置默认值。要同PageEx一同设置回调函数的话,使用PageCallbacks。例子:
PageEx license LicenseText "Readme" LicenseData readme.rtf PageExEnd PageEx license LicenseData license.txt LicenseForceSelection checkbox PageExEnd
扩展页结束 PageExEnd
结束一个PageEx块
页回调函数 PageCallbacks
([creator_function] [leave_function]) | ([pre_function] [show_function] [leave_function])
设置用PageEx定义的页,只能在一个PageEx块内部使用。例子:
PageEx license PageCallbacks licensePre licenseShow licenseLeave PageExEnd
段 Sections
一个NSIS安装程序都有一个或多个段组成,每个段都按照下面的规则进行创建、修改、结束。
每个段包含0个或多个指令
每个段根据安装程序执行的顺序进行,如果设置了ComponentText,用户可以使每个可见的段有效或者失效
如果一个段的名字是“Uninstall”,或者有一个“un.”的前缀,就是一个卸载段
段命令
AddSize
size_kb 告诉安装程序当前段需要一个额外的"size_kb"千字节硬盘空间,这个只在一个段中有效,在外部一个段中或者函数中式无效的。例子:
Section AddSize 500 SectionEnd
段
[/o] [([!]|[-])section_name] [section_index_output]
开始一个段的定义,如果段名字为空、或者忽略了、或者以一个"-"开头,那么将会隐藏这个段,并且用户也不能禁用它,如果设置了section_index_output,这个参数就是!define标识的段序列(这个可以用SectionSetText设置)。如果段名字以“!”开头,那么段将会用粗体显示。如果设置了/o,那么段默认不会被选中。
Section "-hidden section" SectionEnd Section # hidden section SectionEnd Section "!bold section" SectionEnd Section /o "optional" SectionEnd Section "install something" SEC_IDX SectionEnd
要访问段序号,必须使用大括号,并且脚本代码必须位于段的后面。
Section test1 sec1_id SectionEnd Section test2 sec2_id SectionEnd Function .onInit SectionGetText ${sec2_id} $0 MessageBox MB_OK "name of ${sec2_id}:$\n$0" # will correctly display 'name of 1: test2' FunctionEndFunction .onInit SectionGetText ${sec2_id} $0 MessageBox MB_OK "name of ${sec2_id}:$\n$0" # will incorrectly display 'name of ${sec2_id}: test1' # plus a warning stating: # unknown variable/constant "{sec2_id}" detected, ignoring FunctionEnd Section test1 sec1_id SectionEnd Section test2 sec2_id SectionEnd
段结束 SectionEnd
这个表示一个段的结束
段类型 SectionIn
insttype_index [insttype_index] [RO]
这个命令指定当前的段默认属于哪一个安装类型,你也可以为一个段指定多个SectionIn。如果你指定了RO参数,那么这个段就是只读的,用户不能修改它的状态,第一个InstType标号为1,下一个是2,以次类推。
InstType "full" InstType "minimal" Section "a section" SectionIn 1 2 SectionEnd Section "another section" SectionIn 1 SectionEnd
段组 SectionGroup
[/e] section_group_name [index_output]
这个命令插入一个段组,并且必须以SectionGroupEnd结束。它应该包含至少一个段,如果段组名字以“!”开头,那么它将会以粗体的形式显示。如果设置/e,那么默认会展开段组,如果指定了index_output,这个参数是用!define声明的段序号section index。如果名字以“un.”开头,那么这是一个卸载段组。
SectionGroup "some stuff" Section "a section" SectionEnd Section "another section" SectionEnd SectionGroupEnd
段组结束 SectionGroupEnd
关闭一个段组。
卸载段
要生成卸载文件脚本文件中必须有名为“Uninstall”的段。这个段必须移除所有的文件、注册表项等等安装程序写入的数据,例子:
Section "Uninstall" Delete $INSTDIR\Uninst.exe ; delete self (see explanation below why this works) Delete $INSTDIR\myApp.exe RMDir $INSTDIR DeleteRegKey HKLM SOFTWARE\myApp SectionEnd
上面的例子中,第一个Delete指令删除卸载器是有效的,因为卸载器是将会被透明地移动至某一个零时文件夹中执行卸载。
注意在卸载代码中,$INSTDIR包含了卸载程序所在的目录,这个不需要一定是安装程序指定的值。
函数
函数跟段一样,也是包含0到多个指令。用户自定义的函数不会被安装程序直接调用到,只能在段或者函数中调用,回调函数在某些事件出现时被调用。
函数必须在段、其他函数的外部定义。
函数命令
Function
[function_name]
开始定义一个函数,以“.”开头的函数名一般是系统保留的回调函数。以“un.”开头的函数会生成一个卸载函数,因此,一般安装段或者函数不能调用卸载函数,并且卸载段和函数不能调用普通函数。
Function func # some commands FunctionEnd Section Call func SectionEnd
FunctionEnd
这个命令用来结束函数的定义。
回调函数
回调函数的函数名都是以“.”开头的。下面是一个可用的回调函数的名字。
安装程序的回调函数
.onGUIInit 这个函数在安装程序的第一页加载和安装窗口展示之前被调用。允许你修改用户接口。例子:
!include "WinMessages.nsh" Function .onGUIInit # 1028 is the id of the branding text control GetDlgItem $R0 $HWNDPARENT 1028 CreateFont $R1 "Tahoma" 10 700 SendMessage $R0 ${WM_SETFONT} $R1 0 # set background color to white and text color to red SetCtlColors $R0 FFFFFF FF0000 FunctionEnd
.onInit 这个函数在安装程序即将初始化结束的时候调用,如果这个时候函数调用了Abort,那么安装程序将会安静地退出。这个有两个可能用到的例子:
Function .onInit MessageBox MB_YESNO "This will install. Continue?" IDYES NoAbort Abort ; causes installer to quit. NoAbort: FunctionEnd和:
Function .onInit ReadINIStr $INSTDIR $WINDIR\wincmd.ini Configuration InstallDir StrCmp $INSTDIR "" 0 NoAbort MessageBox MB_OK "Windows Commander not found. Unable to get install path." Abort ; causes installer to quit. NoAbort: FunctionEnd
.onInstFailed 这个函数在安装失败(如果不能解压出一个文件,或者安装脚本使用了Abort命令)时用户点击取消按钮时调用。例子:
Function .onInstFailed MessageBox MB_OK "Better luck next time." FunctionEnd
.onInstSuccess 当安装程序成功执行,且在安装程序退出的时候(如果AutoCloseWindow或者SetAutoClose设置为false,可能在用户点击了关闭按钮之后)被调用。
Function .onInstSuccess MessageBox MB_YESNO "Congrats, it worked. View readme?" IDNO NoReadme Exec notepad.exe ; view readme or whatever, if you want. NoReadme: FunctionEnd
.onGUIEnd 这个函数在安装程序的窗口关闭之后被调用,用这个释放所有用户接口相关的插件。
.onMouseOverSection 这个函数在鼠标下的选项更改之后被调用。这可以让你为每一个段设置一个描述,鼠标所在位置的段的id将会被临时放在$0中。
Function .onMouseOverSection FindWindow $R0 "#32770" "" $HWNDPARENT GetDlgItem $R0 $R0 1043 ; description item (must be added to the UI) StrCmp $0 0 "" +2 SendMessage $R0 ${WM_SETTEXT} 0 "STR:first section description" StrCmp $0 1 "" +2 SendMessage $R0 ${WM_SETTEXT} 0 "STR:second section description" FunctionEnd
.onRebootFailed 如果Reboot失败,就会调用这个函数。这个函数中不应该使用WriteUninstall、plug-ins、WriteRegBin。
Function .onRebootFailed MessageBox MB_OK|MB_ICONSTOP "Reboot failed. Please reboot manually." /SD IDOK FunctionEnd
.onSelChanged 在组件选择页中当选项变化的时候被调用,使用SectionSetFlags和SectionGetFlags的时候有用。
段变化包括段选择状态变化和安装类型的变化。
.onUserAbort 当用户取消了安装并且安装没有失败的时候调用。如果这个函数调用了Abort,那么安装程序不会中断。例子:
Function .onUserAbort MessageBox MB_YESNO "Abort install?" IDYES NoCancelAbort Abort ; 这个会让安装程序不退出 NoCancelAbort: FunctionEnd
.onVerifyInstDir 这个函数控制一个安装目录对于你的安装程序是否有效,这个代码会在用户修改安装目录之后被调用,所以不能做一些类似弹出对话框这样的疯狂行为。如果这个函数调用了Abort,那么$INSTDIR被认为无效。
Function .onVerifyInstDir IfFileExists $INSTDIR\Winamp.exe PathGood Abort ; if $INSTDIR is not a winamp directory, don't let us install there PathGood: FunctionEnd
卸载程序的回调函数
un.onGUIInit 这个函数在第一页加载和安装窗口显示之前调用。可以参考.onGUIInit。
un.onInit 这个函数在卸载程序将要初始化结束的时候调用。如果这个函数调用了Abort卸载程序将会安静地退出。如果需要的话这个函数可以确认和修改$INSTDIR。例子:
Function un.onInit MessageBox MB_YESNO "This will uninstall. Continue?" IDYES NoAbort Abort ; causes uninstaller to quit. NoAbort: FunctionEnd和:
Function un.onInit IfFileExists $INSTDIR\myfile.exe found Messagebox MB_OK "Uninstall path incorrect" Abort found: FunctionEnd
un.onUninstFailed 这个函数在卸载程序执行失败(比如使用了Abort命令或者有其他失败)并且用户点击取消的时候调用。例子:
Function un.onUninstFailed MessageBox MB_OK "Better luck next time." FunctionEnd
un.onUninstSuccess 这个函数在卸载程序成功执行完毕,并且在卸载窗口关闭(可能是如果SetAutoClose设置为false)之前调用。例子:
Function un.onUninstSuccess MessageBox MB_OK "Congrats, it's gone." FunctionEnd
un.onGUIEnd 这个函数在卸载窗口关闭之后调用,用这个可以释放是由可能的插件。
un.onRebootFailed 这个函数在Reboot失败的时候调用。这个函数中不能调用WriteUninstall、plug-ins、WriteRegBin。例子:
Function un.onRebootFailed MessageBox MB_OK|MB_ICONSTOP "Reboot failed. Please reboot manually." /SD IDOK FunctionEnd
un.onSelChange 这个函数在组件选择页中选项发生变化的时候被调用,使用SectionSetFlags和SectionGetFlags的时候有用。
un.onUserAbort 这个函数在卸载程序失败并且用户点击取消的时候被调用,如果函数中调用了Abort,那么安装程序不会中断。例子:
Function un.onUserAbort MessageBox MB_YESNO "Abort uninstall?" IDYES NoCancelAbort Abort ; causes uninstaller to not quit. NoCancelAbort: FunctionEnd
安装属性
- 一般属性
- 编译标记
- 版本信息
具体参考用户手册
指令
- 基本指令
- 注册表、INI、文件指令
- 一般目标指令
- 流控制指令
- 文件指令
- 卸载指令
- 杂项
- 字符串指令
- 栈相关指令
- 整型相关
- 重启相关
- 安装日志指令
- 段管理
- 用户界面指令
- 多国语言指令
具体内容参考用户手册
多语言
NSIS版本2就全面支持多语言了,一个安装程序的界面可以支持多个语音。
使用LoadLanguageFile为每一种语言加载默认的接口文本和语言属性。
可以使用像ComponenetText这样的指令修改默认的界面文本。
语言的选择:
- 得到用户界面的默认语言
- 找到最匹配的语言
- 如果没有没完全匹配的语言,那么找一个主要的语言
- 如果没有匹配的语言,使用脚本中第一个定义的语言(保证你的第一个定义的语言是英语这样的普通语言)
- 如果语言变量$LANGUAGE在.onInit函数中变化了,NSIS将会重新执行2-4步。
LangDll插件
LangDll允许你在安装程序中选择语言。只需要push语言的id(${LANG_langfile})和名字为每个语言设置好,然后设置语言个数,标题、还有告诉用户选择语言的提示文本。调用名为LangDialog的plug-in函数,然后pop返回值到$LANGUAGE中,就做好了。如果用户点击了取消按钮,那么返回值就是"cancel"。有一个例子:
languages.nsi
RTL语言; languages.nsi
;
; This is an example of a multilingual installer
; The user can select the language on startup
;--------------------------------
OutFile languages.exe
XPStyle on
RequestExecutionLevel user
;--------------------------------
Page license
Page components
Page instfiles
;--------------------------------
; First is default
LoadLanguageFile "${NSISDIR}\Contrib\Language files\English.nlf"
LoadLanguageFile "${NSISDIR}\Contrib\Language files\Dutch.nlf"
LoadLanguageFile "${NSISDIR}\Contrib\Language files\French.nlf"
LoadLanguageFile "${NSISDIR}\Contrib\Language files\German.nlf"
LoadLanguageFile "${NSISDIR}\Contrib\Language files\Korean.nlf"
LoadLanguageFile "${NSISDIR}\Contrib\Language files\Russian.nlf"
LoadLanguageFile "${NSISDIR}\Contrib\Language files\Spanish.nlf"
LoadLanguageFile "${NSISDIR}\Contrib\Language files\Swedish.nlf"
LoadLanguageFile "${NSISDIR}\Contrib\Language files\TradChinese.nlf"
LoadLanguageFile "${NSISDIR}\Contrib\Language files\SimpChinese.nlf"
LoadLanguageFile "${NSISDIR}\Contrib\Language files\Slovak.nlf"
; License data
; Not exactly translated, but it shows what's needed
LicenseLangString myLicenseData ${LANG_ENGLISH} "bigtest.nsi"
LicenseLangString myLicenseData ${LANG_DUTCH} "waplugin.nsi"
LicenseLangString myLicenseData ${LANG_FRENCH} "example1.nsi"
LicenseLangString myLicenseData ${LANG_GERMAN} "example2.nsi"
LicenseLangString myLicenseData ${LANG_KOREAN} "gfx.nsi"
LicenseLangString myLicenseData ${LANG_RUSSIAN} "languages.nsi"
LicenseLangString myLicenseData ${LANG_SPANISH} "LogicLib.nsi"
LicenseLangString myLicenseData ${LANG_SWEDISH} "makensis.nsi"
LicenseLangString myLicenseData ${LANG_TRADCHINESE} "one-section.nsi"
LicenseLangString myLicenseData ${LANG_SIMPCHINESE} "primes.nsi"
LicenseLangString myLicenseData ${LANG_SLOVAK} "silent.nsi"
LicenseData $(myLicenseData)
; Set name using the normal interface (Name command)
LangString Name ${LANG_ENGLISH} "English"
LangString Name ${LANG_DUTCH} "Dutch"
LangString Name ${LANG_FRENCH} "French"
LangString Name ${LANG_GERMAN} "German"
LangString Name ${LANG_KOREAN} "Korean"
LangString Name ${LANG_RUSSIAN} "Russian"
LangString Name ${LANG_SPANISH} "Spanish"
LangString Name ${LANG_SWEDISH} "Swedish"
LangString Name ${LANG_TRADCHINESE} "Traditional Chinese"
LangString Name ${LANG_SIMPCHINESE} "Simplified Chinese"
LangString Name ${LANG_SLOVAK} "Slovak"
Name $(Name)
; Directly change the inner lang strings (Same as ComponentText)
LangString ^ComponentsText ${LANG_ENGLISH} "English component page"
LangString ^ComponentsText ${LANG_DUTCH} "Dutch component page"
LangString ^ComponentsText ${LANG_FRENCH} "French component page"
LangString ^ComponentsText ${LANG_GERMAN} "German component page"
LangString ^ComponentsText ${LANG_KOREAN} "Korean component page"
LangString ^ComponentsText ${LANG_RUSSIAN} "Russian component page"
LangString ^ComponentsText ${LANG_SPANISH} "Spanish component page"
LangString ^ComponentsText ${LANG_SWEDISH} "Swedish component page"
LangString ^ComponentsText ${LANG_TRADCHINESE} "Traditional Chinese component page"
LangString ^ComponentsText ${LANG_SIMPCHINESE} "Simplified Chinese component page"
LangString ^ComponentsText ${LANG_SLOVAK} "Slovak component page"
; Set one text for all languages (simply don't use a LangString)
CompletedText "Languages example completed"
; A LangString for the section name
LangString Sec1Name ${LANG_ENGLISH} "English section #1"
LangString Sec1Name ${LANG_DUTCH} "Dutch section #1"
LangString Sec1Name ${LANG_FRENCH} "French section #1"
LangString Sec1Name ${LANG_GERMAN} "German section #1"
LangString Sec1Name ${LANG_KOREAN} "Korean section #1"
LangString Sec1Name ${LANG_RUSSIAN} "Russian section #1"
LangString Sec1Name ${LANG_SPANISH} "Spanish section #1"
LangString Sec1Name ${LANG_SWEDISH} "Swedish section #1"
LangString Sec1Name ${LANG_TRADCHINESE} "Trandional Chinese section #1"
LangString Sec1Name ${LANG_SIMPCHINESE} "Simplified Chinese section #1"
LangString Sec1Name ${LANG_SLOVAK} "Slovak section #1"
; A multilingual message
LangString Message ${LANG_ENGLISH} "English message"
LangString Message ${LANG_DUTCH} "Dutch message"
LangString Message ${LANG_FRENCH} "French message"
LangString Message ${LANG_GERMAN} "German message"
LangString Message ${LANG_KOREAN} "Korean message"
LangString Message ${LANG_RUSSIAN} "Russian message"
LangString Message ${LANG_SPANISH} "Spanish message"
LangString Message ${LANG_SWEDISH} "Swedish message"
LangString Message ${LANG_TRADCHINESE} "Trandional Chinese message"
LangString Message ${LANG_SIMPCHINESE} "Simplified Chinese message"
LangString Message ${LANG_SLOVAK} "Slovak message"
;--------------------------------
;Section names set by Language strings
;It works with ! too
Section !$(Sec1Name) sec1
MessageBox MB_OK $(Message)
SectionEnd
; The old, slow, wasteful way
; Look at this section and see why LangString is so much easier
Section "Section number two"
StrCmp $LANGUAGE ${LANG_ENGLISH} 0 +2
MessageBox MB_OK "Installing English stuff"
StrCmp $LANGUAGE ${LANG_DUTCH} 0 +2
MessageBox MB_OK "Installing Dutch stuff"
StrCmp $LANGUAGE ${LANG_FRENCH} 0 +2
MessageBox MB_OK "Installing French stuff"
StrCmp $LANGUAGE ${LANG_GERMAN} 0 +2
MessageBox MB_OK "Installing German stuff"
StrCmp $LANGUAGE ${LANG_KOREAN} 0 +2
MessageBox MB_OK "Installing Korean stuff"
StrCmp $LANGUAGE ${LANG_RUSSIAN} 0 +2
MessageBox MB_OK "Installing Russian stuff"
StrCmp $LANGUAGE ${LANG_SPANISH} 0 +2
MessageBox MB_OK "Installing Spanish stuff"
StrCmp $LANGUAGE ${LANG_SWEDISH} 0 +2
MessageBox MB_OK "Installing Swedish stuff"
StrCmp $LANGUAGE ${LANG_TRADCHINESE} 0 +2
MessageBox MB_OK "Installing Traditional Chinese stuff"
StrCmp $LANGUAGE ${LANG_SIMPCHINESE} 0 +2
MessageBox MB_OK "Installing Simplified Chinese stuff"
StrCmp $LANGUAGE ${LANG_SLOVAK} 0 +2
MessageBox MB_OK "Installing Slovak stuff"
SectionEnd
;--------------------------------
Function .onInit
;Language selection dialog
Push ""
Push ${LANG_ENGLISH}
Push English
Push ${LANG_DUTCH}
Push Dutch
Push ${LANG_FRENCH}
Push French
Push ${LANG_GERMAN}
Push German
Push ${LANG_KOREAN}
Push Korean
Push ${LANG_RUSSIAN}
Push Russian
Push ${LANG_SPANISH}
Push Spanish
Push ${LANG_SWEDISH}
Push Swedish
Push ${LANG_TRADCHINESE}
Push "Traditional Chinese"
Push ${LANG_SIMPCHINESE}
Push "Simplified Chinese"
Push ${LANG_SLOVAK}
Push Slovak
Push A ; A means auto count languages
; for the auto count to work the first empty push (Push "") must remain
LangDLL::LangDialog "Installer Language" "Please select the language of the installer"
Pop $LANGUAGE
StrCmp $LANGUAGE "cancel" 0 +2
Abort
FunctionEnd
InstallOptions::dialog "ini_file_location.ini"
- SilentInstall和SilentUninstall
- SetSilent
- 在命令行里面传递/S(区分大小写)
# This example shows how to handle silent installers.# In short, you need IfSilent and the /SD switch for MessageBox to make your installer# really silent when the /S switch is used.Name "Silent"OutFile "silent.exe"RequestExecutionLevel user# uncomment the following line to make the installer silent by default.; SilentInstall silentFunction .onInit# `/SD IDYES' tells MessageBox to automatically choose IDYES if the installer is silent# in this case, the installer can only be silent if the user used the /S switch or if# you've uncommented line number 5MessageBox MB_YESNO|MB_ICONQUESTION "Would you like the installer to be silent from now on?" \/SD IDYES IDNO no IDYES yes# SetSilent can only be used in .onInit and doesn't work well along with `SetSilent silent'yes:SetSilent silentGoto doneno:SetSilent normaldone:FunctionEndSectionIfSilent 0 +2MessageBox MB_OK|MB_ICONINFORMATION 'This is a "silent" installer'# there is no need to use IfSilent for this one because the /SD switch takes care of thatMessageBox MB_OK|MB_ICONINFORMATION "This is not a silent installer" /SD IDOK# when `SetOverwrite on' (which is the default) is used, the installer will show a message# if it can't open a file for writing. On silent installers, the ignore option will be# automatically selected. if `AllowSkipFiles off' (default is on) was used, there is no# ignore option and the cancel option will be automatically selected.# on is default; AllowSkipFiles on# lock fileFileOpen $0 $TEMP\silentOverwrite w# try to extract - will failFile /oname=$TEMP\silentOverwrite silent.nsi# unlcokFileClose $0# this will always show on silent installers because ignore is the option automatically# selected when a file can't be opened for writing on a silent installerMessageBox MB_OK|MB_ICONINFORMATION "This message box always shows if the installer is silent"AllowSkipFiles off# lock fileFileOpen $0 $TEMP\silentOverwrite w# try to extract - will failFile /oname=$TEMP\silentOverwrite silent.nsi# unlcokFileClose $0SectionEnd
foo.exe /S /D=C:\Program Files\Foo
!include FileFunc.nsh !insertmacro GetParameters !insertmacro GetOptions Function .onInit ${GetParameters} $R0 ClearErrors ${GetOptions} $R0 /USERNAME= $0 FunctionEnd
foo.exe /S /USERNAME=Bar /D=C:\Program Files\Foo
or:
foo.exe /S /USERNAME=string with spaces /D=C:\Program Files\Foo
or:
foo.exe /S /USERNAME="string with spaces" /D=C:\Program Files\Foo
[/NONFATAL] file
!include WinMessages.nsh
!include Library.nsh
!include C:\MyConfig.nsi
!include ..\MyConfig.nsh
!include /NONFATAL file_that_may_exist_or_not.nsh
directory
!addincludedir ..\include !include something.nsh
directory
!addplugindir myplugin MyPlugin::SomeFunction
file text
!tempfile FILE !appendfile "${FILE}" "XPStyle on$\n" !appendfile "${FILE}" "Name 'test'$\n" !include "${FILE}" !delfile "${FILE}" !undef FILE
new_path
file
!tempfile FILE !delfile "${FILE}" !undef FILE
message
!echo "hello world"
[message]
!ifdef VERSION & NOVERSION !error "both VERSION and NOVERSION are defined" !endif
command
!execute '"%WINDIR%\notepad.exe" "${NSISDIR}\license.txt"'
tempfile command
!packhdr "$%TEMP%\exehead.tmp" '"C:\Program Files\UPX\upx.exe" "$%TEMP%\exehead.tmp"'
command [compare comparevalue]
!system '"%WINDIR%\notepad.exe" "${NSISDIR}\license.txt"' !system 'echo !define something > newinclude.nsh' !include newinclude.nsh !ifdef something !echo "something is defined" !endif
symbol
!tempfile PACKHDRTEMP !packhdr "${PACKHDRTEMP}" '"C:\Program Files\UPX\upx.exe" "${PACKHDRTEMP}"'
!tempfile FILE !define /date DATE "%H:%M:%S %d %b, %Y" !system 'echo built on ${DATE} > "${FILE}"' File /oname=build.txt "${FILE}" !delfile "${FILE}" !undef FILE !undef DATE
[message]
!ifdef USE_DANGEROUS_STUFF !warning "using dangerous stuff" !endif
level | push | pop
!verbose push !verbose 1 !include WinMessages.nsh !verbose pop
Section test !ifdef ${__GLOBAL__} !error "this shouldn't be here!" !endif SectionEnd Function test !ifdef ${__GLOBAL__} !error "this shouldn't be here!" !endif FunctionEnd PageEx instfiles !ifdef ${__GLOBAL__} !error "this shouldn't be here!" !endif PageExEnd
!ifdef __SECTION__ !error "this shouldn't be here!" !endif Section test !ifndef __SECTION__ !error "missing predefine!" !endif !if ${__SECTION__} != test !error "wrong predefine value!" !endif SectionEnd Section !test !if ${__SECTION__} != test !error "wrong predefine value!" !endif SectionEnd Section un.test !if ${__SECTION__} != test !error "wrong predefine value!" !endif SectionEnd
!ifdef __FUNCTION__ !error "this shouldn't be here!" !endif Function test !ifndef __FUNCTION__ !error "missing predefine!" !endif !if ${__FUNCTION__} != test !error "wrong predefine value!" !endif FunctionEnd Function un.test !if ${__FUNCTION__} != test !error "wrong predefine value!" !endif FunctionEnd
!ifdef __PAGEEX__ !error "this shouldn't be here!" !endif PageEx instfiles !ifndef __PAGEEX__ !error "missing predefine!" !endif !if ${__PAGEEX__} != instfiles !error "wrong page type" !endif PageExEnd
!ifdef __UNINSTALL__ !error "this shouldn't be here!" !endif Function test !ifdef __UNINSTALL__ !error "this shouldn't be here!" !endif FunctionEnd Function un.test !ifndef __UNINSTALL__ !error "missing predefine!" !endif FunctionEnd
!define symbol_one ${symbol_two}
([/date|/utcdate] gflag [value]) | (/math gflag val1 OP val2) | (/file gflag filename.txt)
gflag
!define SOMETHING !undef SOMETHING
gflag [bcheck gflag [...]]]
!define SOMETHING !ifdef SOMETHING !echo "SOMETHING is defined" !endif !undef SOMETHING !ifdef SOMETHING !echo "SOMETHING is defined" # will never be printed !endif
gflag [bcheck gflag [...]]]
[!] value [op value2]
!if 1 < 2 !echo "1 is smaller than 2!!" !else if ! 3.1 > 1.99 !error "this line should never appear" !else !error "neither should this" !endif
gflag [bcheck gflag [...]]]
!macro SomeMacro !macroend !ifmacrodef SomeMacro !echo "SomeMacro is defined" !endif
gflag [bcheck gflag [...]]]
[if|ifdef|ifndef|ifmacrodef|ifmacrondef [...]]
!ifdef VERSION OutFile installer-${VERSION}.exe !else OutFile installer.exe !endif
macro_name [parameter] [...]
!macro Print text DetailPrint "${text}" !macroend !insertmacro Print "some text" !insertmacro Print "some more text"
macro_name [parameter][...]
!macro SomeMacro parm1 parm2 parm3 DetailPrint "${parm1}" MessageBox MB_OK "${parm2}" File "${parm3}" !macroend
[/ignorecase] [/noerrors] [/file] source_string_or_file substring_start OUTPUTSYMBOL1 [substring [OUTPUTSYMBOL2 [substring ...]]]
# search filename.cpp for a line '#define APP_VERSION "2.5"' and set ${VER_MAJOR} to 2, ${VER_MINOR} to 5. !searchparse /file filename.cpp `#define APP_VERSION "` VER_MAJOR `.` VER_MINOR `"`
[/ignorecase] symbol_out source_string searchfor replacewith
# defines ${blah} to "i like ponies" !searchreplace blah "i love ponies" "love" "like"
- 现代用户界面
- DLL/TLB库的安装
- 示例脚本
- 一些有用的头
- 许可证