程序打包 之 NSIS脚本

程序打包 之 NSIS脚本  

2013-12-04 16:30:19|  分类: 学习笔记|举报|字号 订阅

下载LOFTER客户端
这段时间负责公司的程序的打包,于是就学习了一下NSIS的脚本,结果是英文的,所以就有了翻译一下,顺便学习一下的想法。
编写NSIS程序打包脚本,只需要用一个文本编辑器,最好是带有行号的,这样会有助于读懂编译错误提示。下面开始学习打包。


1. NSIS脚本的基本结构


installer attribute 安装属性:这个决定了安装程序的行为、外观。
Page 页:对于非静默安装的程序来说,一般会要求用户做一些安装确认,那么这个命令就有用了。这个命令可以用来让用户确认license,选择安装组件等。
Sections 段:一般来说,一个程序会有很多东西要安装。比如一些dll库,文本文件,示例等。这些组件都有它们对应的代码,如果用户选择了安装这些组件,那么相应的代码就会被调用。在NSIS脚本中,这个代码写在Section段中。段的名字就显示在组件名称中。如果组件被选中,那么段中的代码就会被执行。当然你也可以将代码卸载一个段中,但是如果你想让用户选择组件的话,那你就必须把代码写在不同的段中。
卸载代码也可以写在不同的段中,卸载段都以"un."开头,比如:
Section "un.Uninstaller Section"
    ;脚本代码
SectionEnd
Functions 函数:函数也可以包含脚本代码,但是函数跟段的不同之处在于调用方式。函数有两种类型用户定义函数和回调函数。
     用户定义函数可以被段或者被其他函数使用call命令调用,这类函数除非你主动调用它们,否则它们不会被执行。安装程序会在你使用Call命令调用函数之后,继续执行Call命令之后的代码,除非在函数调用了终止安装代码。如果你的安装程序有很多脚本命令,那么函数是很有用的。如果你把代码放在函数中,你会节约拷贝时间,并且可以更好地维护代码。
     回调函数在某些事件触发时被调用。但是回调函数是可选项,不是必须要写的。比如,你想要在安装程序的时候弹出一个欢迎界面,你可以定义.onInit函数。NSIS编译器就会在安装开始的时候调用这个函数。
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_test

Goto将会跳转到"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'
FunctionEnd

Function .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

安装属性
    
    
  1. 一般属性
  2. 编译标记
  3. 版本信息
具体参考用户手册
指令
    
    
  1. 基本指令
  2. 注册表、INI、文件指令
  3. 一般目标指令
  4. 流控制指令
  5. 文件指令
  6. 卸载指令
  7. 杂项
  8. 字符串指令
  9. 栈相关指令
  10. 整型相关
  11. 重启相关
  12. 安装日志指令
  13. 段管理
  14. 用户界面指令
  15. 多国语言指令
具体内容参考用户手册

多语言
NSIS版本2就全面支持多语言了,一个安装程序的界面可以支持多个语音。
使用LoadLanguageFile为每一种语言加载默认的接口文本和语言属性。
可以使用像ComponenetText这样的指令修改默认的界面文本。
语言的选择:
    
    
  1. 得到用户界面的默认语言
  2. 找到最匹配的语言
  3. 如果没有没完全匹配的语言,那么找一个主要的语言
  4. 如果没有匹配的语言,使用脚本中第一个定义的语言(保证你的第一个定义的语言是英语这样的普通语言)
  5. 如果语言变量$LANGUAGE在.onInit函数中变化了,NSIS将会重新执行2-4步。
LangDll插件
LangDll允许你在安装程序中选择语言。只需要push语言的id(${LANG_langfile})和名字为每个语言设置好,然后设置语言个数,标题、还有告诉用户选择语言的提示文本。调用名为LangDialog的plug-in函数,然后pop返回值到$LANGUAGE中,就做好了。如果用户点击了取消按钮,那么返回值就是"cancel"。有一个例子:
languages.nsi
; 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
RTL语言
从右向左书写的语言(比如阿拉伯语和希伯来语)。NSIS全面支持RTL语言,语言文件中有一个地方用来设置一个语言是否是RTL语言。 如果要在运行时检查一个语言是否是RTL语言,可以检查$(^RTL)语言字符串。如果是RTL语言这个值是1,否则是0。使用插件创建对话框的时候这个会很有用的,它们一般有RTL设置。

插件
NSIS可以通过插件来增强功能,最常用的插件就是随每一个NSIS发行版一起打包的InstallOptions.dll。
当NSIS运行的时候,它会扫描插件目录,生成一个插件列表并导出它们的函数,编译的时候,如果发现像fred::flintstone这样的字符串,那么就会在这个列表中寻找语言的关键字,如果列表中有fred.dll这个插件并且插件中有flintstone这个导出函数,那么这个插件将会被NSIS打包进安装文件中。
插件命令执行的时候,NSIS会在临时文件夹中解压出必要的dll文件,压入所有指定的参数(从右向左),然后执行执行DLL函数。
使用插件命令
插件命令类似于:
InstallOptions::dialog "ini_file_location.ini"
所有的参数都被压入栈中了,上面的例子中使用了一个参数。有些插件命令可能不需要栈中的参数,也有使用多个参数的命令,要使用插件你必须查看插件的说明,看看参数的含义,使用规范。
手动调用插件
如果你要手动调用用户本地的插件,使用CallInstDLL,几乎所有插件都提供了安装功能,所以使用插件很容易。当你创建了一个需要链接到某个版本的程序中的插件,并且把它拷贝到安装目录的时候,那么CallInstDLL可以派上用场了。


静默安装/卸载

静默安装 不需要用户介入,也没有用户界面,用户看不到任何对话框、也不会被问到任何问题。这个对于网络管理员是非常有用的,他不需要介入任何用户就可以对对台计算机进行程序的安装和卸载。对于那些要把其他程序集成到他们的程序中的开发者来说这也是很有用的。
NSIS的安装程序和卸载程序都是可以静默安装的。当一个安装程序或者卸载程序是静默安装的时候,并非所有回调函数都会被调用,.onGUIInit, .onGUIEnd, un.onGUIInit, un.onGUIEnd以及依赖和页有关的回调函数都不会被调用。
这里有几个方法可以让安装/卸载程序静默执行:
  • SilentInstall和SilentUninstall
  • SetSilent
  • 在命令行里面传递/S(区分大小写)
如果要检查安装/卸载程序是不是静默的,可以使用IfSilent
为了保证你的安装/卸载程序是按需静默的,你应该在每条可能要求用户交互或者创建窗口的命令之前使用IfSilent做检查。silent.nsi示例文件全面演示了这个话题
# 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 silent
Function .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 5
MessageBox 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 silent
Goto done
no:
SetSilent normal
done:
FunctionEnd
Section
IfSilent 0 +2
MessageBox 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 that
MessageBox 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 file
FileOpen $0 $TEMP\silentOverwrite w
# try to extract - will fail
File /oname=$TEMP\silentOverwrite silent.nsi
# unlcok
FileClose $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 installer
MessageBox MB_OK|MB_ICONINFORMATION "This message box always shows if the installer is silent"
AllowSkipFiles off
# lock file
FileOpen $0 $TEMP\silentOverwrite w
# try to extract - will fail
File /oname=$TEMP\silentOverwrite silent.nsi
# unlcok
FileClose $0
SectionEnd
静默安装的时候不能显示路径页,用户可以在 指定命令行里 用一个选项来指定安装路径,这一点在静默和非静默的安装卸载程序中都有效。具体看下面的演示:
foo.exe /S /D=C:\Program Files\Foo
如果你的安装/卸载程序在静默执行的时候需要收集更多的信息,你可以让用户在命令行中指定并在.onInit中处理,可以使用Getopetions来实现:
!include FileFunc.nsh
!insertmacro GetParameters
!insertmacro GetOptions

Function .onInit
  ${GetParameters} $R0
  ClearErrors
  ${GetOptions} $R0 /USERNAME= $0
FunctionEnd
上面的例子将会把用户输入的参数/USERNAME传给$0,这样就让用户通过命令行指定需要的信息,而不需要用户的界面交互,用户可以使用:
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
当然,如果你有很多信息需要在静默状态下让用户输入,最好使用一个文件,这样要比把这些信息写在命令行中好多了。


4. 编译时命令


编译实体命令
这些命令和C预处理器在目的和功能上是一样的。它们可实现文件包含、条件编译、可执行都打包、编译时处理。注意这些命令都不允许使用变量。
!include
[/NONFATAL] file
这个命令将会包含一个文件就像该文件就在原文件中一样。注意,如果一个文件在另一个目录下,当前目录还是脚本所在的目录。如果编译器找不到文件,它将会搜索每一个包含目录,!addincludedir可以用来添加这个包含目录。如果使用了/nonfatal开关,并且没有找到任何文件,将会在编译时提示一个警告而不是错误。
!include WinMessages.nsh
!include Library.nsh
!include C:\MyConfig.nsi
!include ..\MyConfig.nsh
!include /NONFATAL file_that_may_exist_or_not.nsh
!addincludedir
directory
这个用来添加包含文件的搜索路径
!addincludedir ..\include
!include something.nsh
!addpugindir
directory
这个命令用来添加插件的搜索目录
!addplugindir myplugin
MyPlugin::SomeFunction
!appendfile
file text
追加文本到某一个文件。
!tempfile FILE
!appendfile "${FILE}" "XPStyle on$\n"
!appendfile "${FILE}" "Name 'test'$\n"
!include "${FILE}"
!delfile "${FILE}"
!undef FILE
!cd 
new_path
这个用来切换编译器到一个新的路径,new_path可以是相对目录也可以是绝对目录
!delfile
file
这个命令用来删除一个文件
!tempfile FILE
!delfile "${FILE}"
!undef FILE
!echo
message
这个命令会在编译脚本时弹出一个消息给用户。
!echo "hello world"
!error
[message]
这个命令会给编译器发出一个错误,并且停止执行脚本,你可以给这个错误添加一个消息。
!ifdef VERSION & NOVERSION
  !error "both VERSION and NOVERSION are defined"
!endif
!execute
command
这个命令将会用CreateProcess()执行command,不同于!system,它不使用命令行处理器,所以不能使用输入输出重定向和类似cd、dir、和type的命令。这个命令会忽略执行的命令的返回值。当前使用execute命令比system命令的好处在于当当前工作目录是使用UNC指定的时候它不会造成任何麻烦。
在POSIX平台上,execute将会使用system一样的system()。
!execute '"%WINDIR%\notepad.exe" "${NSISDIR}\license.txt"'
!packhdr
tempfile command
这个命令会让编译器使用一个外部的EXE打包工具(比如说Petite或者UPX)来压缩exe文件的头部,指定一个临时文件名(比如说temp.data)和一个命令行(比如说C:\Program Files\upx\upx-9 temp.data)来压缩头部。
!packhdr "$%TEMP%\exehead.tmp" '"C:\Program Files\UPX\upx.exe" "$%TEMP%\exehead.tmp"'
!system
command [compare comparevalue]
这个命令将会使用system()来执行command,如果返回值和comparevalue相比较是false的话,执行将会挂起。compare将会可以是<, >, <>,  =。
!system '"%WINDIR%\notepad.exe" "${NSISDIR}\license.txt"'
!system 'echo !define something > newinclude.nsh'
!include newinclude.nsh
!ifdef something
  !echo "something is defined"
!endif
!tempfile
symbol
这个命令创建一个临时文件,它会把它的路径压入一个定义的命名的符号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
!warning
[message]
这个命令将会给编译器触发一个警告,你也可以给这个警告添加一个消息。
!ifdef USE_DANGEROUS_STUFF
  !warning "using dangerous stuff"
!endif
!verbose
level | push | pop
这个命令将会设置警告的等级。4=all,3=no script,2=no info,1=no warning,0= none
传递push将会是该命令压当前的值到一个指定的栈,传递pop将会使该命令从同一个栈上弹出当前的值并使用它。
!verbose push
!verbose 1
!include WinMessages.nsh
!verbose pop
预定义编译时命令
使用这些标准的预定义命令可以自动添加编译时间等到开发版本里面。
${__FILE__}
当前脚本名字

${__LINE__}
 当前行号

${__DATE__}
开始编译的日期,这个取决于当前的locale

${__TIME__}
开始编译的时间,这个取决于当前的locale

${__TIMESTAMP__}
最后一次修改脚本文件的日期和时间,取决于当前的locale

${NSIS_VERSION}
编译脚本的NSIS版本

预编译作用域
预定义了当前代码范围
${__GLOBAL__}
定义在全局范围:
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
${__SECTION__}
定义为不带任何前缀的段名称,在段作用域中
!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
${__FUNCTION__}
定义为不带前缀的函数名称,在函数作用域中
!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
${__PAGEEX__}
在页作用域中定义页类型
!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
${__UNINSTALL__}
定义在卸载程序的段、函数、页中
!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
读取环境变量
$%envVarname%
$%envVarname%将会在编译时被环境变量名取代。
条件编译
编译器会维护一系列定义符号,它们可以用!define来定义,或者在命令行中使用/D来开关,这些定义的符号可以用作条件编译(使用!ifdef),或者符号替代(一个简单的宏)。使用这个值来替代符号的方法是使用${SYMBOL},如果这个符号没有定义,就不会发生任何替代。这个替换的规则是先到先得,就是说:
!define symbol_one ${symbol_two}
如果使用symbol_two的时候已经定义了,那么它就会被替换。否则任何使用${symbol_one}的地方将会被替换。
定义和条件编译的相关命令
!define 
([/date|/utcdate] gflag [value]) | (/math gflag val1 OP val2) | (/file gflag filename.txt)
这个命令将会添加gflag到全局的预定义列表中。这个和使用/D开关的效果类似。
其他命令参数参见手册
!undef
gflag
从全局的定义列表中除去gflag定义。注意这时候在SYMBOL取消定义之后,在${SYMBOL}使用的地方就会变成“${SYMBOL}”
!define SOMETHING
!undef SOMETHING
!ifdef
gflag [bcheck gflag [...]]]
这个命令将会和!endif成对出现,告诉编译器是否编译这两行之间的脚本。如果定义了就编译,否则就跳过。
!define SOMETHING
!ifdef SOMETHING
  !echo "SOMETHING is defined"
!endif
!undef SOMETHING
!ifdef SOMETHING
  !echo "SOMETHING is defined" # will never be printed
!endif
!ifndef
gflag [bcheck gflag [...]]]
这个含义跟!ifdef相反。如果指定的gflag没有定义就编译脚本。
!if
[!] value [op value2]
这个命令跟!endif成对出现,告诉编译器是否编译脚本,如果值是非零的、或者根据比较结果为true或false来决定编译或者跳过脚本。op可以是==, !=,(字符串的对比),<=, <, >, >=(浮点类型的比较),&&或者||(布尔比较),如果使用了!符号,那么结果将会在true和false之间切换。

!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
!ifmacrodef
gflag [bcheck gflag [...]]]
这个命令和!endif成对出现,告诉编译器是否要编译脚本,如果gflag存在,那么包含的脚本将会被编译,否则就跳过,bcheck可以设置为&(布尔操作and),或者|(布尔操作or),这样就可以联合更多的gflag。执行顺序是从左到右。
!macro SomeMacro
!macroend
!ifmacrodef SomeMacro
  !echo "SomeMacro is defined"
!endif
!ifmacrondef
gflag [bcheck gflag [...]]]
这个命令和上一个相反,如果gflag没有定义,那么就编译。
!else
[if|ifdef|ifndef|ifmacrodef|ifmacrondef [...]]
这个命令让我们可以根据不同的宏定义写不同的代码,你可以创建想!ifdef/!else/!endif, !ifdef/!else ifdef/!else/!endif 等的脚本块。
!ifdef VERSION
OutFile installer-${VERSION}.exe
!else
OutFile installer.exe
!endif
!endif
这个命令用来结束!if, !ifdef, !ifndef, !ifmacrodef, !ifmacrondef命令。
!insertmacro
macro_name [parameter] [...]
插入用!macro定义的宏的内容,如果创建的宏带参数,你就必须传递要求数量的参数。
!macro Print text
  DetailPrint "${text}"
!macroend
!insertmacro Print "some text"
!insertmacro Print "some more text"
!macro
macro_name [parameter][...]
创建一个名为macro_name的宏,所有位于!macro和!macroend之间的行会被保存,之后要使用宏的话,使用!insertmacro,!macro可以带上任意数量的参数。
!macro SomeMacro parm1 parm2 parm3
  DetailPrint "${parm1}"
  MessageBox MB_OK "${parm2}"
  File "${parm3}"
!macroend
!macroend
结束一个以!macro开头的宏定义
!searchparse
[/ignorecase] [/noerrors] [/file] source_string_or_file substring_start OUTPUTSYMBOL1 [substring [OUTPUTSYMBOL2 [substring ...]]]
拆分source_string_or_file(这个将会被看做一个字符串,如果设置了/file就看做是一个文件名),寻找substring_start。如果找到了substring_start,那么OUTPUTSYMBOL1定义为余下的字符串(去掉所有其他可能被找到的子字符串),可以指定任意数量的OUTPUTSYMBOLx。结束的子字符串是可选的。
具体定义参考手册
# 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 `"`
!serachreplace
[/ignorecase] symbol_out source_string searchfor replacewith
搜索source_string,找到searchfor,并用repleacewith替换。
# defines ${blah} to "i like ponies"
!searchreplace blah "i love ponies" "love" "like"
以上基本即时NSIS的全部命令,还有一些其他专题比如:
  • 现代用户界面
  • DLL/TLB库的安装
  • 示例脚本
  • 一些有用的头
  • 许可证
这些可以参考手册。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值