首先看一看NSIS的最简单的例子提供了什么,NSIS的Example目录下有一个例子叫做:example1.nsi,用文本编辑器打开它,其内容为:
; 注释说明 ;-------------------------------- ; 安装程序的名字,该名字会显示在安装程序对话框的标题中 Name "Example1" ; 安装程序的最终文件名,编译后,所有文件被打包生成一个独立的安装程序,名叫example1.exe OutFile "example1.exe" ; 缺省安装到的目录,一般为C:\Program Files\Example1 InstallDir $PROGRAMFILES\Example1 ;-------------------------------- ; 页,表示安装程序的对话框一共会变化几页,一般首页是版权信息,然后第二页让用户选择安装目录, ; 接下来安装文件等等。这个例子只有两个页,选择目录,和复制文件。 Page directory Page instfiles ;-------------------------------- ; 每页有若干节(section),各个节内部才真正进行各种操作 Section "" ;由于没有让用户选择需要安装的组件,所以节的名字可以忽略,脚本将从这里真正执行 ; 安装程序解包后,将文件复制到OutPath中。本例将文件复制到用户选择的安装目录内 SetOutPath $INSTDIR ; 将与脚本处于同一目录下的文件压缩打包到安装程序中,将来用户安装时, ; 这些文件会被解包复制到OutPath里 File example1.nsi ; 将脚本自己打包 SectionEnd ; 本节结束
这个例子非常简单,他把脚本自己打包进example1.exe中,然后可以发布这个example1.exe,它本身是一个安装程序。用户在自己的计算 机上一旦运行此程序,就会出现一个对话框,让用户选择安装目录,用户选择好后,其会将example1.nsi从包内解出,并复制到用户选择好的安装目录 内。可以用这个脚本做一个测试,有两个方法可以编译脚本生成可执行的安装程序,一个方法时在这个脚本上点鼠标右键,选择“Compile NSIS script”,然后NSIS编译器就会启动,编译脚本打包文件生成example1.exe,第二个方法是在Eclipse内选择菜单 EclipseNSIS | Compile Script,或者使用快键Alt+C,也会生成example.exe。双击这个exe就会运行此安装程序将example.nsi复制安装的指定目 录。
在这个例子的基础上,很容易写出我自己的安装程序。
例子脚本和我的要求有若干不同,主要如下:
我需要从网络下载zip到用户计算机
我需要亲自解开zip包到用户目录
我需要建立桌面和程序快捷方式
我需要建立卸载程序
首先针对第1点要求,经过查阅资料,发现NSIS带有一个下载插件可以完成此功能,其典型用法是:
NSISdl::download http://www.yoursite.org/yourpack.zip "$INSTDIR\yourpack.zip" Pop $R0 ; 获得下载结果的返回值 StrCmp $R0 success download_ok SetDetailsView show DetailPrint "download failed: $R0" Abort download_ok: DetailPrint "download $R0 OK"
上面这段代码的意思是,首先调用NSISdl::download这个插件去网络上下载yourpack.zip文件到用户本地的安装目录下,并且文件名保持不变。是否下载成功的结果放在寄存器变量$R0中,如果下载成果这个变量中的指应该是字符串success,所以StrCmp的作用就是比较返回结果是否成功,如果成功,就跳转到download_ok标记,否则如果下载失败(包括网络问题等等),就打印出失败信息,并且终止安装。
这段代码具有一定的代表性,实际上可以利用它从网络下载多个文件到用户计算机,唯一不同的就是下载地址和保存到本地的文件名。因此可以把它制作成一个函数,如下:
Function DownloadFile NSISdl::download $remote_zip_file "$INSTDIR\$local_zip_file" ; 略... download_ok: DetailPrint "download $R0 OK" FunctionEnd
这个函数的参数,用两个全局变量$remote_zip_file和$local_zip_file传入。调用方法如下:
Var remote_zip_file Var local_zip_file StrCpy $remote_zip_file "http://ftp.squeak.org/current_stable/win/Squeak-Win32-3.7.1-VM.zip" StrCpy $local_zip_file "Squeak-Win32-3.7.1-VM.zip" Call DownloadFile
NSIS中的变量用Var语句声明,声明时不带有$前缀,使用时,利用$前缀进行引用,这一点和Unix的Shell一样,赋值不采用等号,而使用StrCpy语句,进行字符串复制。
至此第一个需求就可以得到完全满足了,我可以分别下载Squeak的虚拟机和Squeak的映像到用户的计算机,此时的安装脚本如下:
; 安装程序的名称 Name "Squeak 3.9 Installer" ; 安装程序的文件名 OutFile "squeak3.9_win_installer.exe" ; 缺省安装目录 InstallDir "$PROGRAMFILES\Squeak3.9" ;-------------------------------- ; 安装对话框包含的页内容 ; Pages Page directory Page instfiles ;-------------------------------- ; 自定义的全局变量 Var remote_zip_file Var local_zip_file ; 安装Section Section "" ; 安装输出的目录 SetOutPath $INSTDIR ; 从网络分别下载虚拟机和映像 Call DownloadVM Call DownloadImage SectionEnd ; Section结束 ; 下载虚拟机 Function DownloadVM StrCpy $remote_zip_file "http://ftp.squeak.org/current_stable/win/Squeak-Win32-3.7.1-VM.zip" StrCpy $local_zip_file "Squeak-Win32-3.7.1-VM.zip" Call DownloadFile FunctionEnd ; 下载映像 Function DownloadImage StrCpy $remote_zip_file "http://ftp.squeak.org/3.9/Squeak3.9-RC2-7064.zip" StrCpy $local_zip_file "Squeak3.9-RC1-7064.zip" Call DownloadFile Call ExtractFile FunctionEnd ; 下载文件的通用函数 Function DownloadFile NSISdl::download $remote_zip_file "$INSTDIR\$local_zip_file" Pop $R0 ; Get the return value StrCmp $R0 success download_ok SetDetailsView show DetailPrint "download failed: $R0" Abort download_ok: DetailPrint "download $R0 OK" FunctionEnd
读者可以使用NSIS编译这个脚本并运行安装程序,它会将虚拟机和映像的zip文件下载到指定目录下(例如C:\Program Files\Squeak3.9\)并结束。下面着手实现第二个需求,将zip包解开。查阅NSIS手册,没有发现类似功能的函数或者插件。其原因是, NSIS在打包时,会自动将普通文件按照zip标准的压缩算法打包,而执行exe时,则自动解包。所以NSIS没有考虑将zip再次打包,以及显式解压缩 zip文件的功能。经过在网络上使用搜索引擎查找,发现了一个第三方插件NsisUnz[3],其可以从这里下载http://home.no.net/nxs/nsis/nsisunz.7z,该文件是7zip格式,可以在这里下载其压缩解压缩工具:http://www.7-zip.org/。解开软件包后,将其中的nsisunz.dll复制到NSIS目录下的Plugins子目录下即可(例如C:\Program Files\NSIS\Plugins\),该Plugin的使用方式为:
nsisunz::UnzipToLog "$INSTDIR\myfile.zip" "$INSTDIR"
若解压缩成功,该命令返回字符串success,否则表示解压缩失败。因此可以仿照上面的下载函数,写出一个通用的解压缩函数,它接受一个zip文件的文件名字符串,然后对其解压缩,如下:
Function ExtractFile nsisunz::UnzipToLog "$INSTDIR\$local_zip_file" "$INSTDIR" Pop $R0 StrCmp $R0 "success" unzip_ok DetailPrint "$R0" Abort unzip_ok: DetailPrint "extract $R0 OK" Delete "$INSTDIR\local_zip_file" FunctionEnd
这个解压缩函数一旦成功,就会删除原来下载的zip文件,以节约用户空间。采用这个通用的解压缩函数,可以修改上面的虚拟机和映像下载函数为:
Function DownloadVM StrCpy $remote_zip_file "http://ftp.squeak.org/current_stable/win/Squeak-Win32-3.7.1-VM.zip" StrCpy $local_zip_file "Squeak-Win32-3.7.1-VM.zip" Call DownloadFile Call ExtractFile FunctionEnd Function DownloadImage StrCpy $remote_zip_file "http://ftp.squeak.org/3.9/Squeak3.9-RC2-7064.zip" StrCpy $local_zip_file "Squeak3.9-RC1-7064.zip" Call DownloadFile Call ExtractFile FunctionEnd
编译这个改进的脚本并运行,可以发现zip被下载后解开成各自的文件了。这里有一些小问题,有些zip包里,就包含一些文件,但是有些zip包里,却包含目录,例如上面两个zip包解开后,在$INSTDIR下就会出现这样的文件结构:
+Squeak.exe
+SqueakFFIPrims.dll
+Squeak3.9-RC2-7064\
+Squeak3.9-RC2-7064.image
+Squeak3.9-RC2-7064.changes
+SqueakV39.source
+WelcomeSqueak39
而我需要所有这6个文件,都在同一目录下,因此需要使用NSIS的Rename函数移动这些文件。为此修改DownloadImage函数如下:
Function DownloadImage ;略... Call DownloadFile Call ExtractFile ; 将文件移动到$INSTDIR下,并删除空掉的子目录 Rename "$INSTDIR\Squeak3.9-RC2-7064\Squeak3.9-RC2-7064.changes" "$INSTDIR\Squeak3.9-RC2-7064.changes" Rename "$INSTDIR\Squeak3.9-RC2-7064\Squeak3.9-RC2-7064.image" "$INSTDIR\Squeak3.9-RC2-7064.image" Rename "$INSTDIR\Squeak3.9-RC2-7064\SqueakV39.sources" "$INSTDIR\SqueakV39.sources" Rename "$INSTDIR\Squeak3.9-RC2-7064\WelcomeSqueak39" "$INSTDIR\WelcomeSqueak39" RMDir "$INSTDIR\Squeak3.9-RC2-7064" FunctionEnd
至此,第二个需求就满足了,为了方便用户使用,下面实现第3个需求——在桌面和程序菜单中建立Squeak的快捷方式,这样用户只需要双击这些快捷方式的图标,就可以运行Squeak了。squeak的运行方式是按照这样的命令行:
squeak.exe squeak_iamge_filename.image
但是Windows的目录名可能会带有空格,例如C:\ Program Files\Squeak3.9\中,Program和Files之间就存在一个空格,这样squeak.exe会把空格前的部分,C:\Program 当作映像文件名,而把后面的Files\Squea…当作参数,这就出现了歧义。为此需要把路径用””括起来。但是NSIS中,会把””忽略掉,办法 是在””外再括一层”。此后就可以使用NSIS的CreateShortCut函数创建快捷方式了。当必要的文件下载并解压缩成功后就进行这一步的内 容,为此修改Section如下:
Section "" SetOutPath $INSTDIR Call DownloadVM Call DownloadImage ; 创建快捷方式 CreateDirectory "$SMPROGRAMS\Squeak\3.9\" CreateShortcut "$SMPROGRAMS\Squeak\3.9\squeak.lnk" $INSTDIR\Squeak.exe '"$INSTDIR\Squeak3.9-RC2-7064.image"' CreateShortcut "$DESKTOP\squeak3.9.lnk" $INSTDIR\Squeak.exe '"$INSTDIR\Squeak3.9-RC2-7064.image"' Exec '$INSTDIR\Squeak.exe "$INSTDIR\Squeak3.9-RC2-7064.image"' ; 安装成功后自动运行Squeak SetAutoClose true ; 安装结束后自动关闭安装程序 SectionEnd
为了进一步增强用户友好性,还在安装成功后,自动运行Squeak并关闭安装程序。
最后,良好的安装程序还应该提供卸载功能,并干净地清除用户计算机上的内容。为此可以利用NSIS提供的 UnistPage命令创建卸载程序页面,并提供一个名叫Uninstall的Section实际进行卸载工作。对于Squeak这点卸载工作的内容包 括,删除程序文件,删除快捷方式等,其实现如下:
; 用于安装的页 Page directory Page instfiles ; 用于卸载的页 UninstPage uninstConfirm UninstPage instfiles ;-------------------------------- ; 略... Section "Uninstall" ; 删除文件 Delete "$INSTDIR\*.image" Delete "$INSTDIR\*.changes" Delete "$INSTDIR\*.dll" Delete "$INSTDIR\*.sources" Delete "$INSTDIR\*.exe" Delete "$INSTDIR\WelcomeSqueak39" RMDir "$INSTDIR" ; 删除快捷方式 Delete "$SMPROGRAMS\Squeak\3.9\squeak.lnk" Delete "$DESKTOP\squeak3.9.lnk" RMDir "$SMPROGRAMS\Squeak\3.9" SetAutoClose true SectionEnd 提供Uninstall功能的脚本,需要在安装的最后一步身成uninstall.exe供用户日后使用,为此在安装Section最后增加这样一行: CreateShortcut "$SMPROGRAMS\Squeak\3.9\uninstall.lnk" "$INSTDIR\uninstall.exe" WriteUninstaller "$INSTDIR\uninstall.exe" 并在Uninstall的Section中增加对卸载快捷方式的删除动作: Delete "$SMPROGRAMS\Squeak\3.9\uninstall.lnk"
至此一个完整的安装程序就制作完成了