NSIS (NullSoft Scriptable Install System)使用指南

首先看一看NSIS的最简单的例子提供了什么,NSIS的Example目录下有一个例子叫做:example1.nsi,用文本编辑器打开它,其内容为:

; 注释说明
 ;--------------------------------
 ; 安装程序的名字,该名字会显示在安装程序对话框的标题中
 Name "Example1"
 ; 安装程序的最终文件名,编译后,所有文件被打包生成一个独立的安装程序,名叫example1.exe
 OutFile "example1.exe"
 ; 缺省安装到的目录,一般为C:Program FilesExample1
 InstallDir $PROGRAMFILESExample1
 ;--------------------------------
 ; 页,表示安装程序的对话框一共会变化几页,一般首页是版权信息,然后第二页让用户选择安装目录,
 ; 接下来安装文件等等。这个例子只有两个页,选择目录,和复制文件。
 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 "$INSTDIRyourpack.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 "$PROGRAMFILESSqueak3.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 FilesSqueak3.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 FilesNSISPlugins),该Plugin的使用方式为:

nsisunz::UnzipToLog "$INSTDIRmyfile.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 "$INSTDIRlocal_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 "$INSTDIRSqueak3.9-RC2-7064Squeak3.9-RC2-7064.changes" "$INSTDIRSqueak3.9-RC2-7064.changes"
 Rename "$INSTDIRSqueak3.9-RC2-7064Squeak3.9-RC2-7064.image" "$INSTDIRSqueak3.9-RC2-7064.image"
 Rename "$INSTDIRSqueak3.9-RC2-7064SqueakV39.sources" "$INSTDIRSqueakV39.sources"
 Rename "$INSTDIRSqueak3.9-RC2-7064WelcomeSqueak39" "$INSTDIRWelcomeSqueak39"
 RMDir "$INSTDIRSqueak3.9-RC2-7064"
 FunctionEnd

至此,第二个需求就满足了,为了方便用户使用,下面实现第3个需求——在桌面和程序菜单中建立Squeak的快捷方式,这样用户只需要双击这些快捷方式的图标,就可以运行Squeak了。squeak的运行方式是按照这样的命令行:

squeak.exe squeak_iamge_filename.image

但是Windows的目录名可能会带有空格,例如C: Program FilesSqueak3.9中,Program和Files之间就存在一个空格,这样squeak.exe会把空格前的部分,C:Program 当作映像文件名,而把后面的FilesSquea…当作参数,这就出现了歧义。为此需要把路径用””括起来。但是NSIS中,会把””忽略掉,办法是在””外再括一层”。此后就可以使用NSIS的CreateShortCut函数创建快捷方式了。当必要的文件下载并解压缩成功后就进行这一步的内容,为此修改Section如下:

Section ""
 SetOutPath $INSTDIR
 Call DownloadVM
 Call DownloadImage
 ; 创建快捷方式
 CreateDirectory "$SMPROGRAMSSqueak3.9"
 CreateShortcut "$SMPROGRAMSSqueak3.9squeak.lnk" $INSTDIRSqueak.exe
 '"$INSTDIRSqueak3.9-RC2-7064.image"'
 CreateShortcut "$DESKTOPsqueak3.9.lnk" $INSTDIRSqueak.exe
 '"$INSTDIRSqueak3.9-RC2-7064.image"'
 Exec '$INSTDIRSqueak.exe "$INSTDIRSqueak3.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 "$INSTDIRWelcomeSqueak39"
 RMDir "$INSTDIR"
 ; 删除快捷方式
 Delete "$SMPROGRAMSSqueak3.9squeak.lnk"
 Delete "$DESKTOPsqueak3.9.lnk"
 RMDir "$SMPROGRAMSSqueak3.9"

SetAutoClose true
 SectionEnd

提供Uninstall功能的脚本,需要在安装的最后一步身成uninstall.exe供用户日后使用,为此在安装Section最后增加这样一行:

CreateShortcut "$SMPROGRAMSSqueak3.9uninstall.lnk" "$INSTDIRuninstall.exe"
 WriteUninstaller "$INSTDIRuninstall.exe"

并在Uninstall的Section中增加对卸载快捷方式的删除动作:

Delete "$SMPROGRAMSSqueak3.9uninstall.lnk"

至此一个完整的安装程序就制作完成了,该安装脚本的完整代码可以在这里下载。squeak3.9_win_install.nsi

NSIS 是“Nullsoft 脚本安装系统”(Nullsoft s criptable Installation System) 的缩写,它是一个免费的 Win32 安装、卸载系统,采用了简洁高效的脚本方式。它本来是一个由 Nullsoft 创建并用于作为 Winamp 及其插件发布的系统,但现已被上百个应用程序所应用,作为它们进行程序发布的工具。

NSIS 创建的安装程序可以进行安装、卸载、设置系统设置、解压文件等等。几乎可以做所有事情。因为它基于脚本文件,所以你可以完全控制你的安装程序的每个部分。它的脚本语言支持变量、函数、字串处理,就像是一个普通的程序语言 – 但其仅设计用来创建安装程序。

简体中文增强版除了主程序的翻译之外还把主要的文档作了翻译并修正了官方版本里简体中文语言习惯用语不合适之处。
单纯的使用脚本来编写安装程序对于大多数人来说不仅困难而且容易出错,为此,简体中文增强版里集成了一个相当好用的脚本编辑器 – VNISEdit。只需要根据向导就能轻松的做出很酷的安装程序,甚至,你还可以在向导里给你的安装程序添加启动画面、让你的安装程序在安装过程中播放音乐,而这,只需要点几下鼠标即可。除此之外,VNISEdit 附带的注册表转换插件还可以把 .reg 文件一次性的转换为 NSIS 脚本,补丁制作向导可以一次性地为你生成体积超小的补丁升级程序。简体中文增强版还在官方版本的基础上集成了一些有用的插件,比如 FindProc 可以根据可执行文件名查找进程是否在运行,这在制作补丁程序或者是汉化包时非常有用。由于各个组件在使用时各自独立,所以强烈推荐安装全部组件
使用NSIS制作安装文件

1.前言

如果你是第一次制作安装程序,建议你使用 Setup Factory 或 CreateInstall
因为这两个安装程序是图形接口非常的适合初学者使用。笔者一开始也不会使用
NSIS 写脚本而是先从上面两套软件着手,了解安装程序的流程与基础才进而使用
Nullsoft Installation System 来制作安装程序。

同一个程序用这三套软件以最精简的流程制作出来的安装程序其大小如下

———————————————–
原始程序             248 KB
Setup Factory         488 KB
CreateInstall          156 KB
Nullsoft Installation System  134 KB
———————————————–

我本来是想直接用 CreateInstall 来制作安装程序就好因为它是图形接口简单
作出来的档案也很小。但是他作出来的安装程序在繁体中文系统底下有一个BUG
让我非放弃他不可。就是建立快捷方式在「开始」菜单 -> 程序集 里他会因为
BIG5 里面的 “功” 含有 “” 而建立成 「开始」-> 能表 -> 程序集 少了一个
字作出来的快捷方式也不能用 >_<” 想办法跟作者联络但是我英文实在很破…….
前言到此结束底下开始是教学了

==========================================================

本教学以 KYO 网友所著的 ShellInfoTip 为例子,因为它需要做到下列三项

1.注册 DLL 档案
2.注册 REG 登录档案
3.在 WIN 路径里放置 STPlugin.ini

我先在这里简单的描述所需用到的参数在文章结尾处我会放上 ShellInfoTip.nsi
脚本档案我有写上详细的批注,这里以提出所需要的三项功能作为主要介绍。有
不知道不了解不清楚或我没提及的参数,你可以查找 NSIS 的使用说明书他写的
非常详细。

1.注册 DLL 档案

RegDLL "$INSTDIRShellTip4Rar.dll"

其中 $INSTDIR 是代表你安装的目录
如:你是装在 c:empShellInfoTip 底下,他的 $INSTDIR 就是这个目录会随着你的安装路径而变动。

2.注册 REG 登录档案

WriteRegStr HKCR ".rarShellEx{ 00021500-0000-0000-c000-000000000046 }" "" "{ C34C25BE-9B2B-4A50-9D97-2658E0D8796B }"
 WriteRegStr HKCR ".exeShellEx{ 00021500-0000-0000-c000-000000000046 }" "" "{ C34C25BE-9B2B-4A50-9D97-2658E0D8796B }"
 WriteRegStr HKCR ".001ShellEx{ 00021500-0000-0000-c000-000000000046 }" "" "{ C34C25BE-9B2B-4A50-9D97-2658E0D8796B }"

WriteRegStr 是写入类型为 REG_SZ 的数值
HKCR 是 HKEY_CLASSES_ROOT 的缩写

原始程序所要注册的登录档内容如下
———————————————————————–
REGEDIT4

[HKEY_CLASSES_ROOT.rarShellEx]

[HKEY_CLASSES_ROOT.rarShellEx{ 00021500-0000-0000-c000-000000000046 }]
@=”{ C34C25BE-9B2B-4A50-9D97-2658E0D8796B }”

[HKEY_CLASSES_ROOT.exeShellEx]

[HKEY_CLASSES_ROOT.exeShellEx{ 00021500-0000-0000-c000-000000000046 }]
@=”{ C34C25BE-9B2B-4A50-9D97-2658E0D8796B }”

[HKEY_CLASSES_ROOT.001ShellEx]

[HKEY_CLASSES_ROOT.001ShellEx{ 00021500-0000-0000-c000-000000000046 }]
@=”{ C34C25BE-9B2B-4A50-9D97-2658E0D8796B }”
———————————————————————–

3.在 WIN 路径里放置 STPlugin.ini

WriteINIStr "$WINDIRSTPlugin.ini" "Plugin" "Plugin1" "$INSTDIRrar_txt.dll"

$WINDIR 是 Windows 系统的目录
WriteINIStr 是写入 INI 档案,按照顺序写入 ini 的文件名称、区域名称、变量名称、数值。

原始程序的 STPlugin.ini 档内容如下
————————————————-
[Plugin]
Plugin1=安装路径名rar_txt.dll
————————————————-

三项主要的功能介绍就到此结束了底下开始是ShellInfoTip.nsi脚本档案的内容

;宣告软件名称,后面可以利用 ${ NAME } 调用这个名字
 !define NAME "ShellInfoTip"

;设定字型
 SetFont 宋体 9

;使用 WindowsXP 视觉样式
 XPstyle on

;安装窗口的标题名称
 Caption /LANG=2052 "安装 Winrar Shell Info Tip 功能"

;替换预设的按钮文字
 MiscButtonText /LANG=2052 "< 上一步" "下一步 >" "取消" "关闭"

;替换预设的按钮文字
 InstallButtonText /LANG=2052 "安装"

;替换预设的按钮文字
 UninstallButtonText /LANG=2052 "反安装"

;替换反安装程序的文字
 DirText /LANG=2052 "欢迎您安装 Shell Info Tip 这是个自由软件版权归原作者 KYO 所有。此安装程序由∮Ω奥米加空间∮ 坛主 DC 提供制作。" "请选择欲安装 ${ NAME } 的目录:" "浏览..."

;替换反安装程序的文字
 UninstallText /LANG=2052 "现在将从你的系统中反安装 ${ NAME } 。如果你触发了 ${ NAME } 的功能 ShellTip4Rar.dll 动态连结文件必须重开机之后才可以被删除。" "反安装目录:"

;替换反安装程序标题的文字
 UninstallCaption /LANG=2052 "反安装 ${ NAME }"

;替换反安装程序的文字
 DetailsButtonText /LANG=2052 "显示详细过程"

;替换反安装按钮的文字
 UninstallButtonText /LANG=2052 "反安装"

;反安装程序显示方式 预设是隐藏
 ShowUninstDetails hide

;替换空间的文字
 SpaceTexts /LANG=2052 "所需的空间 " "可用的空间 "

;这个安装程序的名称
 Name "ShellInfoTip"

;输出制作完成的安装程序档案
 OutFile "ShellInfoTipchs.exe"

;预设的安装程序目录在 Program Files 里
 InstallDir $PROGRAMFILES${ NAME }

;底下开始是安装程序所要执行的
 Section "ThisNameIsIgnoredSoWhyBother?"

;设定输出的路径在安装程序的目录
 SetOutPath $INSTDIR

;贴上你所要包装在安装程序里的档案
 File "C:All ReleaseYingInstallFilesrar_txt.dll"
 File "C:All ReleaseYingInstallFilesShellTip4Rar.dll"

;注册 DLL 档案
 RegDLL "$INSTDIRShellTip4Rar.dll"

;写入欲注册的值在 HKEY_CLASSES_ROOT 里类型为 REG_SZ
 WriteRegStr HKCR ".rarShellEx{ 00021500-0000-0000-c000-000000000046 }" "" "{ C34C25BE-9B2B-4A50-9D97-2658E0D8796B }"
 WriteRegStr HKCR ".exeShellEx{ 00021500-0000-0000-c000-000000000046 }" "" "{ C34C25BE-9B2B-4A50-9D97-2658E0D8796B }"
 WriteRegStr HKCR ".001ShellEx{ 00021500-0000-0000-c000-000000000046 }" "" "{ C34C25BE-9B2B-4A50-9D97-2658E0D8796B }"

;写入 INI 档
 WriteINIStr "$WINDIRSTPlugin.ini" "Plugin" "Plugin1" "$INSTDIRrar_txt.dll"

;在程序集里建立反安装程序的快捷方式
 CreateDirectory "$SMPROGRAMS${ NAME }"
 CreateShortCut "$SMPROGRAMS${ NAME }反安装 ${ NAME }.lnk"
 "$INSTDIRuninst.exe"

;写入建立反安装程序要跟上面所连结的档名一样
 WriteUninstaller "$INSTDIRuninst.exe"

SectionEnd
 ;安装程序过程到此结束

;反安装程序内容
 Section "Uninstall"

;循环删除档案直到删除数据夹
 UnRegDLL "$INSTDIRShellTip4Rar.dll"
 Delete "$INSTDIR*.*"
 Delete "$WINDIRSTPlugin.ini"
 RMDir "$SMPROGRAMS${ NAME }"
 RMDir "$INSTDIR"

;写入欲删除注册的值
 DeleteRegKey HKCR ".rarShellEx"
 DeleteRegKey HKCR ".exeShellEx"
 DeleteRegKey HKCR ".001ShellEx"
 SectionEnd

;反安装程序过程到此结束
2…
; NSIS Modern User Interface version 1.62
; NSIS 多语言脚本写作密技
; 这是 KA 实地用于 MDaemon 4.05 中文化的多语言安装,
; 环境: NSIS 2.0b1, Modern UI 1.62
; 为便于阅读,标明 (* 进阶) 者,较为困难,可先略过。
; 几乎是想办法用上 Modern UI 1.62 新版的所有技巧了。
; 如果注解写得太多,损害你的智商,请赶快写信骂我。
; 简体描述部分,因为在 BIG5 编码之下使用 GB2312 ,故
; 必定有乱码,还请见谅。如果需要在网页取得正确编码,
; 请储存为 BIG5 plain text ,再来处理。我会另外以
; attach file 的方式附在板上。
; 我没有使用任何类似 GUI 的工具。如果你之前已经有用
; 于 2.0a7 的 NSI 脚本,跨越版本时在 Modern UI 的写
; 法,会花一些工夫。比如新世纪精华板有人用的
; magiclime ,现在只能用 advsplash 以外挂程式方式载入。
; 不用怕,我在里面都写了。如果还嫌太麻烦,Joost 的英
; 文版HTML 入门,只有一页,建议先看。
; 记得,没人强迫你要使用多语言,没人强迫你要 Modern
; UI ,没人强迫你要欢迎对话方块。你一样可以做出一点也
; 不花俏的,很传统的安装程式。不要在那里废话什么 NSIS
; 愈做愈肥大。
; 这些都是自己决定的。
;

;=======================================================
 ; ■1.定义变数、安装目录、输出档案、图示
 ;=======================================================
 ; 只要在这里写好,其他的都按照定义的变数下去跑,就省力多了。
 ;
 ;==============================
 ; 1.1 版本号码/名称
 ;
 !define VER_1 4 ;软体版本号码 1 ,如 Eudora 4.3.1.2 四位版本号码
 !define VER_2 05 ;软体版本号码 2
 ;!define VER_3 xxx ;软体版本号码 3 (* 进阶)
 ;!define VER_4 yyy ;软体版本号码 4 (* 进阶)
 ;!define VER_R zzz ;中文化同版本第几更版 (* 进阶)
 !define MUI_PRODUCT "MDaemon" ;程式名称 (可使用大小写)
 !define PROGFNHEADER "mdaemon" ;中文化档案名称的主要部分 (全部小写)
 !define PROGMAINEXE "mdaemon.exe" ;主程式执行档案名称 (含.exe)
 ;
 ;==============================
 ; 1.2 读我档案/输出档案
 ;
 ; 读我显示,包括 CHS/CHT/ENU 三个版本。如果你不需要制作
 ; 多语言中文化版本,只要考虑一个“读我”即可。这个读我,
 ; 通常拿来放“此中文化的描述档案”,而非一般软体的 readme 。
 ;
 ; 档案命名: (全部小写)。
 ; (cpatch) c + ${ PROGFNHEADER } + 版本号码 _(底线) 第几中文化版本 . 副档名
 ; (hanzify) ha _(底线) ${ PROGFNHEADER } + 版本号码 _(底线) 作者名 . 扩展名
 ;
 ;!define README "c${ PROGFNHEADER }${ VER_1 }${ VER_2 }.txt"
 !define README_CHS "c${ PROGFNHEADER }${ VER_1 }${ VER_2 }_gb.txt"
 !define README_CHT "c${ PROGFNHEADER }${ VER_1 }${ VER_2 }.txt"
 !define README_ENU "c${ PROGFNHEADER }${ VER_1 }${ VER_2 }.txt"
 OUTFILE "c${ PROGFNHEADER }${ VER_1 }${ VER_2 }.exe"
 ;==============================
 ; 1.3 预设安装目录 (尾端不加 )
 ;
 ; 通常会出现在 "C:Program Filesxxxx " 这种目录。
 ; 也可以使用 ${ AppPath } 在注册表寻找旧版安装目录。
 ; 如果注册表的安装目录 (尾端 ) 出现,不要使用,以免发生错误。
;InstallDir "$PROGRAMFILES${ MUI_PRODUCT }"
 ;InstallDir "${ AppPath }"
 InstallDir "C:MDaemon"
; 1.4 版权影像
;
; 只需要一个就够 (一定要 bmp 格式),第二个是拿来花俏用的。
; 可以只使用一个,或完全不使用。

!define SPLASH1 "mdaeomon4_cht.bmp"
!define SPLASH2 "mdaeomon4_chs.bmp"

;==============================
; 1.5 安装程式型态
;
; 中文化/直接安装版/还是补缀补丁?安装时出现在 caption 部分。
; 解除安装时,也会出现在注册表。

!define PATCHTYPE_ENU "(CN+TW) localized"
!define PATCHTYPE_CHT "(CN+TW) 中文化"
!define PATCHTYPE_CHS "(CN+TW) 笢恅赵"
;=======================================================

; ■2.使用者介面
;=======================================================
; 这一段的用途,在于强制覆写 system.nsh 的英文预设设定值,
; 通常只有变更 “字型”“图示”
;==============================
; 2.1 字型

; 安装介面的字型预设大小 8,一定要改掉,否则在 win98 中文不好看。
; 如下。双语言中文化,使用 “Tahoma” 即可。
; 不需要特别指定“新宋体”或“宋体”。

;!define MUI_FONT "MS Shell Dlg"
 ;!define MUI_FONTSIZE "8"
 ;!define MUI_FONT_HEADER "MS Sans Serif"
 ;!define MUI_FONTSIZE_HEADER "8"
 ;
 ; 上列: NSIS 字型“预设值”,下列: 字型“建议修改值”。
 ;
 !define MUI_FONT "Tahoma"
 !define MUI_FONTSIZE "9"
 !define MUI_FONT_HEADER "Tahoma"
 !define MUI_FONTSIZE_HEADER "9"

; 2.2 图示

; 安装程式图示: 通常我使用主程式的图示,另外用 VC 抽取出来。
; 解除安装图示: 为了让 [控制台 &line; 新增/移除程式] 出现正确图示,还是用同一个图示。
; 有时候则使用它附的图示 (因为看起来不错)。

;!define MUI_ICON "${ NSISDIR }ContribIconsmodern-install.ico"
 ;!define MUI_UNICON "${ NSISDIR }ContribIconsmodern-uninstall.ico"
 ;
 ; 上列: NSIS 图示“预设值”,风格有点像 InstallShield 6 ,也不错。
 ; 下列: 自订图示,请指定正确名称

!define MUI_ICON "mdaemon4.ico"
 !define MUI_UNICON "mdaemon4.ico"

; 2.3 其他 (* 进阶)

!define MUI_UI "${ NSISDIR }ContribUIsmodern2.exe"
 !define MUI_CHECKBITMAP "${ NSISDIR }ContribIconsmodern.bmp"
 !define MUI_FONTstyle_HEADER "700"
 !define MUI_FONT_TITLE "Verdana"
 !define MUI_FONTSIZE_TITLE "12"
 !define MUI_FONTstyle_TITLE "700"
 !define MUI_INSTALLCOLORS "/windows"
 !define MUI_PROGRESSBAR "smooth"
 !define MUI_SPECIALINI "${ NSISDIR }ContribModern UIioSpecial.ini"
 !define MUI_SPECIALBITMAP "${ NSISDIR }ContribIconsmodern-wizard.bmp"
 !define MUI_BGCOLOR "0xFFFFFF"

;=======================================================
; ■3.多语言系统
;=======================================================
; 载入 Modern UISystem.nsh 之用。
;==========================
; 3.1 前置相同变数,视需要而定。
;
; 通常出现在更换版本时,原脚本的变数校正。

!define MUI_VERSION "${ VER_1 }.${ VER_2 }"
 ;!define NAME "${ MUI_PRODUCT }"

;显示中文化标题,依 ENU/CHT/CHS 语言的不同。

CAPTION /LANG=1033 "${ MUI_PRODUCT } ${ MUI_VERSION } ${ PATCHTYPE_ENU }"
 CAPTION /LANG=1028 "${ MUI_PRODUCT } ${ MUI_VERSION } ${ PATCHTYPE_CHT }"
 CAPTION /LANG=2052 "${ MUI_PRODUCT } ${ MUI_VERSION } ${ PATCHTYPE_CHS }"

; 3.2 载入 Modern UI 系统

!include "${ NSISDIR }ContribModern UISystem.nsh"

; 3.3 使用者介面
;
; 如果不要这些对话方块,不做 define ,就不会出现。

!define MUI_WELCOMEPAGE ;欢迎页面
 !define MUI_LICENSEPAGE ;授权页面
 !define MUI_COMPONENTSPAGE ;元件页面
 !define MUI_DIRECTORYPAGE ;目录选择
 !define MUI_ABORTWARNING ;中止警告
 !define MUI_UNINSTALLER ;解除安装程式
 !define MUI_UNCONFIRMPAGE ;不做确认页面
 ; !define MUI_STARTMENUPAGE ;选择开始功能表
 !define MUI_FINISHPAGE ;出现完成页面,执行程式和读我。
 !define MUI_FINISHPAGE_RUN "$INSTDIRapp${ PROGMAINEXE }"
 !define MUI_FINISHPAGE_SHOWREADME "$INSTDIR${ README_CHT }"
 ; !define MUI_FINISHPAGE_SHOWREADME "$INSTDIR$(README)"

; LangString README ${ LANG_ENGLISH } "${ README_ENU }"
 ; LangString README ${ LANG_TRADCHINESE } "${ README_CHT }"
 ; LangString README ${ LANG_SIMPCHINESE } "${ README_CHS }"
 ; 上面这段 LangString 并不能套用在 SHOWREADME ,真奇怪。

!insertmacro MUI_SYSTEM ;使用者介面系统

; 3.4 各国语言

!insertmacro MUI_LANGUAGE "English"
 !insertmacro MUI_LANGUAGE "SimpChinese"
 !insertmacro MUI_LANGUAGE "TradChinese"

; 3.5 授权文字 (* 进阶)
;
; 这些档案没有出现在 NSIS 预设包内。如果你手上没有 license 档案,
; 请直接 remark 掉。
; 这些东西会先被解压缩出来。保留这几行在任何 File 指令之前!)
; 只能用于 BZIP2 压缩

LicenseData /LANG=${ LANG_ENGLISH } "${ NSISDIR }ExamplesModern UIlicense_enu.rtf"
 LicenseData /LANG=${ LANG_SIMPCHINESE } "${ NSISDIR }ExamplesModern UIlicense_chs.rtf"
 LicenseData /LANG=${ LANG_TRADCHINESE } "${ NSISDIR }ExamplesModern UIlicense_cht.rtf"

!insertmacro MUI_RESERVEFILE_LANGDLL

; ■4.安装程式,安装开始的前置动作 (* 进阶)

; 如果不需要,你可以全部 remark。
 Function .onInit

; 4.1 显示版权影像
;
; 旧版可使用 magiclime 做淡入淡出。不过这一版没了,改成
; advsplash ,外挂方式。解压缩到临时目录,显示 splash ,然后删掉。
; 注意: nsis 2.0a0 有 magicsplash 档案。不过 2.0b1 居然又不见了。

SetOutPath $TEMP
 File /oname=splash1.bmp ${ SPLASH1 }
 File /oname=splash2.bmp ${ SPLASH2 }
 ; File /oname=spltmp.exe "${ NSISDIR }binmagiclime.exe"
 ; ExecWait '"$TEMPspltmp.exe" splash1.bmp'
 ; ExecWait '"$TEMPspltmp.exe" splash2.bmp'

advsplash::show 1000 600 400 -1 $TEMPsplash1
 advsplash::show 1000 600 400 -1 $TEMPsplash2

Pop $0 ; $0 has '1' if the user closed the splash screen early,
 ; '0' if everything closed normal, and '-1' if some error occured.

Delete $TEMPspltmp.exe
 Delete $TEMPsplash1.bmp
 Delete $TEMPsplash2.bmp

; 4.2 字型选择 (* 进阶)
;
; 只影响安装程式的语言选择 dialog ,因此不要使用中文字型名称。

Push Verdana
 Push 9

; 4.3 语言 (* 进阶)
;
; 塞了 ENU/CHT/CHS 三个语言。你要 18 个语言全塞,也不反对。大小大概增加 20 kb 左右。
; 如果不是多语言中文化安装,你可以全部 remark 掉。

!insertmacro MUI_LANGDLL_PUSH "English"
 !insertmacro MUI_LANGDLL_PUSH "SimpChinese"
 !insertmacro MUI_LANGDLL_PUSH "TradChinese"

Push 3F ;3 = 语言数目, F = 变更字型

LangDLL::LangDialog "Installer Language" "Please select a language."

; 上面这一句,不要中文化。除非你想让老外看到乱码。

Pop $LANGUAGE
 StrCmp $LANGUAGE "cancel" 0 +2
 Abort

; 4.4 安装前置动作 (修改注册表) (* 进阶)
;
; 视你需要,可以偷偷做一些动作。

; DeleteRegKey HKLM "SOFTWAREClassesCLSID{ b5eedee0-c06e-11cf-8c56-444553540000 }InProcServer32"
 ; DeleteRegKey HKLM "SOFTWAREClassesCLSID{ b5eedee0-c06e-11cf-8c56-444553540000 }"

; 4.5 寻找旧版已安装的目录。 (* 进阶)
;
; 如果 AppPath 读出的尾端有 符号,请不要直接使用于 INSTDIR 指令,以免发生错误。

; ReadRegStr $AppPath HKLM "SOFTWAREAlt-N TechnologiesMDaemon" "AppPath"

FunctionEnd

; ■5.安装程式,安装完成后置动作 (* 进阶)

; 5.1 函式定义开始

Function .onInstSuccess

; 5.2 安装完成后,执行 ExecShell
;
; 视个人需要而定,我只用在 Eudora 安装而已。

; !macro MUI_INSTSUCCESS1 LANGID TEXT
 ;
 ; StrCmp $LANGUAGE ${ LANGID } "" +3
 ; MessageBox MB_YESNO&line;MB_ICONQUESTION "${ TEXT }" IDNO Nomapi
 ; ExecShell open '$INSTDIRSWMAPI.EXE'
 ; !macroend
; 下列是 ENU/CHT/CHS 三个语言的动作。
 ;
 ; !insertmacro MUI_INSTSUCCESS1 1033 "Do you want to install ${ NAME } as a default mailer? It will excute MAPI installer."
 ; !insertmacro MUI_INSTSUCCESS1 1028 "你想要安装 ${ NAME } 做为预设信件程式吗?将执行外部 MAPI 安装程式。"
 ; !insertmacro MUI_INSTSUCCESS1 2052 "斓砑猁假蚾 ${ NAME } 酕峈苏?;蛔璃最唗镉ˋ蔚堍俴俋窒 MAPI 假蚾最唗﹝"
 ; Nomapi:

; 5.3 安装后,要求使用者读取 README 档案

; !macro MUI_INSTSUCCESS2 LANGID TEXT README

; StrCmp $LANGUAGE ${ LANGID } "" +3
 ; MessageBox MB_YESNO&line;MB_ICONQUESTION "${ TEXT }" IDNO NoReadme
 ; ExecShell open notepad '$INSTDIR${ README }'
 ; !macroend
; 下列是 ENU/CHT/CHS 三个语言的动作。

; !insertmacro MUI_INSTSUCCESS2 1033 "Setup has been completed successfully . Press [Yes] to continue README file." "${ README_ENU }"
 ; !insertmacro MUI_INSTSUCCESS2 1028 "安装已成功地完成。按下 [Yes] 继续读取 README 档案。" "${ README_CHT }"
 ; !insertmacro MUI_INSTSUCCESS2 2052 "假蚾眒伧髡华俇伧﹝偌狟 [Yes] 樟哿黍?; README 恅璃﹝" "${ README_CHS }"
 ; NoReadme:

; 5.4 函式定义结束

FunctionEnd

; ■6.安装的所有档案

; 6.1 安装类型

; 两种应该很够了。

InstType "Minimum"
 InstType "Standard"

SetOverwrite on

; 6.3 安装的各个节段

Section "MDaemon" SecMDaemon
 SectionIn 1 2

; 6.3.1 中文化的读我
; 通常塞在安装的主目录。

SetOutPath $INSTDIR
 File "${ README_CHT }"
 File "${ README_CHS }"

; 6.3.2 中文化的程式档案

; 通常不使用 *.* 型态,而个别指定,以免包装错误。
; 常常是 *.DLL *.EXE *.AX 等类型。我都把这类放在 cht 为首的工作目录。

SetOutPath $INSTDIRapp
 File "chtAccountPrune.exe"
 File "chtCFILTER.EXE"
 File "chtIMPORTNT.EXE"
 File "chtListPrune.exe"
 File "chtMD3CONV.EXE"
 File "chtMDAEMON.EXE"
 File "chtMDCONFIG.EXE"
 File "chtMDLAUNCH.EXE"
 File "chtMDSTATS.EXE"
 File "chtMDUSER.DLL"
 File "chtMDUserLdap.dll"
 File "chtMigrateUsers.exe"
 File "chtROBOEX32.DLL"
 File "chtUninstallSurvey.exe"
 File "chtUNWISE.EXE"
 File "chtWCIMPORT.EXE"
 File "chtXCEEDZIP.DLL"

; 6.3.3 中文化的文字档案
;
; 通常是 (*.cnt, *.txt *.dat *.ini) 类型。
; 无语言区分者,仍放在 pack 为首的工作目录。
; 有语言区分者,放在 text/cht 与 text/chs 为首的工作目录。

; 6.3.3.1 如果有旧版,会有一些“一定”要删除的旧版档案。然后再由安装程式补上新版。

SetOutPath $INSTDIRapp
 Delete "$INSTDIRappHELP.DAT"
 Delete "$INSTDIRappMDAEMON.CNT"
 Delete "$INSTDIRappacctinfo.dat"

File "pack405appCFILTER.INI"
 File "pack405appMDSTATS.INI"

; 6.3.3.2 如果有旧版,也会有一些“不能删除”的旧版档案。

; 这段是使用在 UltraEdit ,把 windows 目录的 INI 移动过来。MDaemon 不用这一段。

; IfFileExists "$WINDIRíit32.ini" DO1 DO2
 ; DO1: ;把 $WINDIR 的移动到安装目录
 ; Rename "$WINDIRíit32.ini" "$INSTDIRíit32.ini"
 ; DO2: ;如果没有,则写入一个假的 INI,以修正 HTMLTIDY 的功能。如果安装目录已有,则不盖掉。
SetOverwrite off ;停用强制覆写
 File "pack405appMDAEMON.INI"
 SetOverwrite on ;判断完毕之后,启用强制覆写

; 6.3.3.3 搞一些好玩的,比如塞入自动使软体注册的资料
; 正式版本就不要这样做,以免太嚣张,反而不好。

; WriteINIStr "$INSTDIRappmdaemon.ini" "Registration" "Name" "My_reg_Name"
 ; WriteINIStr "$INSTDIRappmdaemon.ini" "Registration" "Company" "My_reg_Company"
 ; WriteINIStr "$INSTDIRappmdaemon.ini" "Registration" "4XKey" "GDPFFMF-QAWLYFJ-XQDIYAO"

; 6.3.3.4 补上新版档案
;
; 依照各种语言,自动执行,分配正确的档案。使用者不必再选择。
; 先写一个巨集,自动判断作业系统语言。 1033 enu, 1028 cht, 2052 chs

!macro MUI_COPYTEXTFILE LANGID ACTION_COPYTEXTFILE

StrCmp $LANGUAGE ${ LANGID } "" +3
 ${ ACTION_COPYTEXTFILE }

!macroend

; !insertmacro MUI_COPYTEXTFILE 1033 'File "text405enuHelp.dat"'
 ; !insertmacro MUI_COPYTEXTFILE 1033 'File "text405enuMDaemon.cnt"'

; 英文的不动,只做为对照组。注意单引号和双引号。
; 这种脚本写法其实蛮笨的,应该有更好的方法。

!insertmacro MUI_COPYTEXTFILE 1028 'File "text405chtHelp.dat"'
 !insertmacro MUI_COPYTEXTFILE 1028 'File "text405chtMDaemon.cnt"'
 !insertmacro MUI_COPYTEXTFILE 2052 'File "text405chsHelp.dat"'
 !insertmacro MUI_COPYTEXTFILE 2052 'File "text405chsMDaemon.cnt"'

; 6.3.4 非中文化档案 (不用修改的部分)

; 可使用 *.* 这种型态。 我都把这类放在 pack 为首的工作目录。

SetOutPath $INSTDIRapp
 File "pack405appAccountPrune.txt"
 File "pack405appbwcc32.dll"
 File "pack405appADDUSER.SMP"
 File "pack405appCFAPP.ICO"
 File "pack405appCFILTER.DLL"
 File "pack405appDEDUPE.EXE"
 File "pack405appDIRREORG.EXE"
 File "pack405appDomainPOP.dsc"
 File "pack405appEDITUSER.SMP"
 File "pack405appFILELIST.DAT"
 File "pack405appINETWH32.DLL"
 File "pack405appINSTALL.LOG"
 File "pack405appMailboxSize.exe"
 File "pack405appMailboxSize.txt"
 File "pack405appMDAEMON.DSC"
 File "pack405appMDAEMON.HLP"
 File "pack405appNTUTIL.DLL"
 File "pack405appULREPAIR.EXE"
 File "pack405appWISECHCK.EXE"
 File "pack405appWISEUPDT.EXE"
 File "pack405appWP2WC.EXE"
 File "pack405appXCDSFX32.BIN"

SetOutPath $INSTDIRcgi
 File "pack405cgi*.*"
 SectionEnd

Section "MDaemon Docs" SecMDaemonDocs
 SectionIn 2
 SetOutPath $INSTDIRdocs
 File "pack405docs*.*"
 SetOutPath $INSTDIRdocsapi
 File "pack405docsapi*.*"
 SectionEnd

Section "World Client" SecWorldClient
 SectionIn 2
 SetOutPath $INSTDIRwcstandard
 File "pack405wcstandard*.*"
 ; 底下为需要中文化的部分
 File "chtwcstandardWCStandard.exe"
 File "chtwcstandardWorldClient.dll"
 File "chtwcstandardXceedZip.dll"

SetOutPath $INSTDIRwcstandardhtml
 File "pack405wcstandardhtml*.*"
 SetOutPath $INSTDIRwcstandardhtmlhelp
 File "pack405wcstandardhtmlhelp*.*"
 SetOutPath $INSTDIRwcstandardtemplates
 File "pack405wcstandardtemplates*.*"
 SectionEnd

Section "Web Config" SecWebConfig
 SectionIn 2
 SetOutPath $INSTDIRwebconfig
 File "pack405webconfig*.*"
 ; 底下为需要中文化的部分
 File "chtwebconfigWebConfig.exe"

SetOutPath $INSTDIRwebconfightml
 File "pack405webconfightml*.*"
 SetOutPath $INSTDIRwebconfigtemplates
 File "pack405webconfigtemplates*.*"
 SectionEnd

; 6.4 加入到“开始选单”

Section "Add shortcut to Start Menu" SecStartMenu
 SectionIn 1 2
 SetOutPath "$INSTDIR"

CreateDirectory "$SMPROGRAMS${ MUI_PRODUCT }"
 ; 有时候 CreateShortCut 会出问题,不如用这个动作
 ; 直接强制 installer 建立目录。这样比较贱。

CreateShortCut "$SMPROGRAMS${ MUI_PRODUCT }MDaemon.lnk"
 "$INSTDIRappmdaemon.exe"
 CreateShortCut "$SMPROGRAMS${ MUI_PRODUCT }MDConfig.lnk"
 "$INSTDIRappMDConfig.exe"
 CreateShortCut "$SMPROGRAMS${ MUI_PRODUCT }Uninstall ${ MUI_PRODUCT }.lnk"
 "$INSTDIRUninstall.exe"
 SectionEnd

; 6.5 加入到“快速启动”
;
; 不是每个程式都要加入“快速启动”列,视需要而定。

;Section "Add shortcut to Quicklaunch" SecQuicklaunch
 ; SectionIn 1 2
 ; CreateShortCut "$QUICKLAUNCH${ MUI_PRODUCT }.lnk"
 ; "$INSTDIRapp${ PROGMAINEXE }"
 ;SectionEnd

; 6.6 加入到“桌面”

Section "Add shortcut to Desktop" SecDesktop
 SectionIn 1 2
 CreateShortCut "$DESKTOP${ MUI_PRODUCT }.lnk"
 "$INSTDIRapp${ PROGMAINEXE }"
 SectionEnd

; 6.6 加入到“滑鼠右键” (* 进阶)

;
; 只用在 UltraEdit

;Section "Add to Right mouse button" SecRightMouse
 ; SectionIn 1 2
 ; WriteRegStr HKLM "SOFTWAREClasses*shellexContextMenuHandlersUltraEdit-32" "" "{ b5eedee0-c06e-11cf-8c56-444553540000 }"
 ; WriteRegStr HKLM "SOFTWAREClassesCLSID{ b5eedee0-c06e-11cf-8c56-444553540000 }" "" "UltraEdit-32"
 ; WriteRegStr HKLM "SOFTWAREClassesCLSID{ b5eedee0-c06e-11cf-8c56-444553540000 }InProcServer32" "" "$INSTDIRtmn.dll"
 ; WriteRegStr HKLM "SOFTWAREClassesCLSID{ b5eedee0-c06e-11cf-8c56-444553540000 }InProcServer32" "ThreadingModel" "Apartment"
 ;SectionEnd

; ■7.写入解除安装程式

; 7.1 节段开始

Section "Create Uninstaller" SecCreateUninst
 SectionIn 1 2

; 7.2 写入到“注册表”(基础)
;
; 一般只有写下面这一段,指定安装目录。

WriteRegStr HKLM "SOFTWARE${ MUI_PRODUCT }" "Install_Dir" "$INSTDIR"
 WriteRegStr HKLM "SOFTWARE${ MUI_PRODUCT }" "Installer Language" $LANGUAGE

; 7.3 写入到“注册表” (* 进阶)
;
; 下面这段注册表值,只是在 { 控制台 &line; 新增移除程式] 花俏用的。
; 如果看不懂,可以全部不使用。

WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }"
 "DisplayIcon" "$INSTDIRapp${ PROGMAINEXE }"
 ; "DisplayIcon" "$INSTDIR${ PROGMAINEXE }" ; 一般使用这个型态。只有 MDaemon 例外。
 WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }"
 "DisplayName" "${ MUI_PRODUCT } ${ VER_1 }.${ VER_2 } ${ PATCHTYPE_ENU }"
 WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }"
 "DisplayVersion" "${ VER_1 }.${ VER_2 }"
 WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }"
 "UninstallString" "$INSTDIRuninstall.exe"
 WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }"
 "URLInfoAbout" "http://www.yhxs3344.net/Products/Default.asp?product_id=MDaemon"
 WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }"
 "Contact" "[email protected]"
 WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }"
 "HelpLink" "http://www.yhxs3344.net/Support/Default.asp?product_id=MDaemon"
 WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }"
 "URLUpdateInfo" "http://www.yhxs3344.net/Download/Default.asp"
 WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }"
 "Readme" "$INSTDIR${ README_CHT }"
 WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }"
 "Comments" "Chinese (P.R.C.) and Chinese (Taiwan) ${ MUI_PRODUCT } localized version by Kii Ali, for Mailer Daemon with win32 plateform."
 WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }"
 "Publisher" "Copyright (c) 2002, Alt-N Technology."

; 7.4 依照各种语言,自动写入不同的注册表值 (* 进阶)
; 包括 ENU/CHT/CHS

!macro MUI_REGMUI1 LANGID TEXT

StrCmp $LANGUAGE ${ LANGID } "" +3
 ${ TEXT }

!macroend

!insertmacro MUI_REGMUI1 1033 'WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }" "Comments" "Chinese (P.R.C.) and Chinese (Taiwan) ${ MUI_PRODUCT } localized version by Kii Ali, for Mailer Daemon with win32 plateform." '
 !insertmacro MUI_REGMUI1 1028 'WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }" "Comments" "中文 (中国大陆) 与 中文 (台湾) ${ MUI_PRODUCT } 本地化版本,由 Kii Ali 制作,用于 win32 平台架设个人或公司的信件伺服器。" '
 !insertmacro MUI_REGMUI1 2052 'WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }" "Comments" "笢恅 (笢弊湮翻) 迵 笢恅 (怢俜) ${ MUI_PRODUCT } 挂华赵唳挂ㄛ蚕 Kii Ali 秶钎ㄛ蚚衾 win32 Ⅸ;怢殇扢跺?;么鼠侗腔蛔璃督昢け;﹝" '

; 7.5 依照各种语言,自动写入不同的注册表值 (* 进阶)
; 包括 ENU/CHT/CHS

!macro MUI_REGMUI2 LANGID TEXT

StrCmp $LANGUAGE ${ LANGID } "" +3
 ${ TEXT }

!macroend
 !insertmacro MUI_REGMUI2 1033 'WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }" "Publisher" "Copyright (c) 2002, Alt-N Technology." '
 !insertmacro MUI_REGMUI2 1028 'WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }" "Publisher" "版权所有 (c) 2002, Alt-N Technology. " '
 !insertmacro MUI_REGMUI2 2052 'WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }" "Publisher" "唳?;垀衄 (c) 2002, Alt-N Technology." '

; 7.6 现在写入“解除安装程式”

; 写入语言名称到注册表 (用于解除安装程式)

WriteRegStr HKLM "Software${ MUI_PRODUCT }" "Installer Language" $LANGUAGE
 WriteUninstaller "$INSTDIRUninstall.exe"

SectionEnd

; 7.8 显示“完成”标头

;如果不想使用“完成”页面,在这个巨集之后插入。

!insertmacro MUI_SECTIONS_FINISHHEADER

; ■8.描述功能

; 8.1 描述功能的巨集 (* 进阶)
;
; 如果有描述,最好一个一个加入巨集。
; 这一段要在所有的 section 之后,否则会出现“变数名称没有找到”。
; 如果 MUI_FUNCTIONS_DESCRIPTION_BEGIN 拆成两段,会变成重覆呼叫,而导致错误。

!insertmacro MUI_FUNCTIONS_DESCRIPTION_BEGIN
 !insertmacro MUI_DESCRIPTION_TEXT ${ SecMDaemon } $(DESC_SecMDaemon)
 !insertmacro MUI_DESCRIPTION_TEXT ${ SecMDaemonDocs } $(DESC_SecMDaemonDocs)
 !insertmacro MUI_DESCRIPTION_TEXT ${ SecWorldClient } $(DESC_SecWorldClient)
 !insertmacro MUI_DESCRIPTION_TEXT ${ SecWebConfig } $(DESC_SecWebConfig)
 !insertmacro MUI_DESCRIPTION_TEXT ${ SecStartMenu } $(DESC_SecStartMenu)
 ; !insertmacro MUI_DESCRIPTION_TEXT ${ SecQuicklaunch } $(DESC_SecQuicklaunch)
 !insertmacro MUI_DESCRIPTION_TEXT ${ SecDesktop } $(DESC_SecDesktop)
 !insertmacro MUI_DESCRIPTION_TEXT ${ SecCreateUninst } $(DESC_SecCreateUninst)
 ; !insertmacro MUI_DESCRIPTION_TEXT ${ SecRightMouse } $(DESC_SecRightMouse)
 !insertmacro MUI_FUNCTIONS_DESCRIPTION_END

; 8.2 每个节段的描述文字,直接使用各国语言 (* 进阶)
;
; 这是让滑鼠移动到安装项目上时,自动出现的描述功能。
; 我使用 ENU/CHT/CHS 三个语言。如果嫌麻烦,可以只使用一个语言,甚至不使用描述。

LangString DESC_SecMDaemon ${ LANG_ENGLISH } "MDaemon Server Main Prog"
 LangString DESC_SecMDaemonDocs ${ LANG_ENGLISH } "Related Documents, manuals"
 LangString DESC_SecWorldClient ${ LANG_ENGLISH } "Server: reading your emails on browser"
 LangString DESC_SecWebConfig ${ LANG_ENGLISH } "config server on browser"
 LangString DESC_SecStartMenu ${ LANG_ENGLISH } "Add icon to Start Menu"
 LangString DESC_SecQuicklaunch ${ LANG_ENGLISH } "Add icon to QuickLaunch"
 LangString DESC_SecDesktop ${ LANG_ENGLISH } "Add icon to Desktop"
 LangString DESC_SecRightMouse ${ LANG_ENGLISH } "Add function of mouse right-click"
 LangString DESC_SecCreateUninst ${ LANG_ENGLISH } "Create Uninstaller"

LangString DESC_SecMDaemon ${ LANG_TRADCHINESE } "MDaemon 伺服器主程式"
 LangString DESC_SecMDaemonDocs ${ LANG_TRADCHINESE } "相关的文件、手册"
 LangString DESC_SecWorldClient ${ LANG_TRADCHINESE } "浏览器网页读取信件的伺服器"
 LangString DESC_SecWebConfig ${ LANG_TRADCHINESE } "在浏览器组态伺服器"
 LangString DESC_SecStartMenu ${ LANG_TRADCHINESE } "加入图示到开始功能表"
 LangString DESC_SecQuicklaunch ${ LANG_TRADCHINESE } "加入图示到快速启动列"
 LangString DESC_SecDesktop ${ LANG_TRADCHINESE } "加入图示到桌面"
 LangString DESC_SecRightMouse ${ LANG_TRADCHINESE } "加入滑鼠右键功能"
 LangString DESC_SecCreateUninst ${ LANG_TRADCHINESE } "建立解除安装程式"

LangString DESC_SecMDaemon ${ LANG_SIMPCHINESE } "MDaemon 督昢け;翋最唗"
 LangString DESC_SecMDaemonDocs ${ LANG_SIMPCHINESE } "眈寿腔恅璃﹜忒聊"
 LangString DESC_SecWorldClient ${ LANG_SIMPCHINESE } "銡拟け;厍珜黍?;蛔璃腔督昢け;"
 LangString DESC_SecWebConfig ${ LANG_SIMPCHINESE } "婓銡拟け;餍离督昢け;"
 LangString DESC_SecStartMenu ${ LANG_SIMPCHINESE } "氖楼芞梓善※羲宎§粕等"
 LangString DESC_SecQuicklaunch ${ LANG_SIMPCHINESE } "氖楼芞梓善办厒ぎ;雄蹈"
 LangString DESC_SecDesktop ${ LANG_SIMPCHINESE } "氖楼芞梓善袤拨"
 LangString DESC_SecRightMouse ${ LANG_SIMPCHINESE } "氖楼扷梓衵莹髡夔"
 LangString DESC_SecCreateUninst ${ LANG_SIMPCHINESE } "斐膘贱壶假蚾最唗"

; ■9.解除安装程式,真正的动作

; 这个部分,和前面的建立 CreateUninst ,意味不一样喔。
; 前者: 把 uninstaller 程式做出来。
; 后者: 指定这个程式,要删除那些档案/目录/注册表项。

; 9.1 节段开始

Section "Uninstall"

; 要移除掉解除安装程式
 Delete "$INSTDIRUninstall.exe"
 ; 移除档案
 Delete "$INSTDIRApp*.*"
 Delete "$INSTDIRLogs*.*"
 Delete "$INSTDIRdocsapiCreateUser*.*"
 Delete "$INSTDIRdocsapiIterateUsers*.*"
 Delete "$INSTDIRdocsapiSample COMMDUserEdit*.*"
 Delete "$INSTDIRdocsapiSample COM*.*"
 Delete "$INSTDIRdocsapi*.*"
 Delete "$INSTDIRdocs*.*"
 Delete "$INSTDIRcgi*.*"
 Delete "$INSTDIRARCHIVES*.*"
 Delete "$INSTDIRBACKUP*.*"
 Delete "$INSTDIRBADMSGS*.*"
 Delete "$INSTDIRCFILTER*.*"
 Delete "$INSTDIRDIGESTS*.*"
 Delete "$INSTDIRGATEWAYS*.*"
 Delete "$INSTDIRLOCALQLNDOMAIN*.*"
 Delete "$INSTDIRLOCALQ*.*"
 Delete "$INSTDIRLOGS*.*"
 Delete "$INSTDIRPUBLIC~1*.*"
 Delete "$INSTDIRRAWFILES*.*"
 Delete "$INSTDIRREMOTEQRETRY*.*"
 Delete "$INSTDIRREMOTEQ*.*"
 Delete "$INSTDIRSMTPIN*.*"
 Delete "$INSTDIRWCstandardHTMLhelp*.*"
 Delete "$INSTDIRWCstandardHTML*.*"
 Delete "$INSTDIRWCstandardTemp*.*"
 Delete "$INSTDIRWCstandardTemplates*.*"
 Delete "$INSTDIRWCstandard*.*"
 Delete "$INSTDIRwebconfigHTML*.*"
 Delete "$INSTDIRwebconfigTemp*.*"
 Delete "$INSTDIRwebconfigTemplates*.*"
 Delete "$INSTDIRwebconfig*.*"
 Delete "$INSTDIR*.*"

; 移除开始功能表、桌面、快递启动等捷径。
 Delete "$SMPROGRAMS${ MUI_PRODUCT }*.*"
 Delete "$DESKTOP${ MUI_PRODUCT }.lnk"
 Delete "$QUICKLAUNCH${ MUI_PRODUCT }.lnk"
 RMDir "$SMPROGRAMS${ MUI_PRODUCT }"

; 移除使用过的目录。
 RMDir "$INSTDIRApp"
 RMDir "$INSTDIRLogs"
 RMDir "$INSTDIRdocsapiCreateUser"
 RMDir "$INSTDIRdocsapiIterateUsers"
 RMDir "$INSTDIRdocsapiSample COMMDUserEdit"
 RMDir "$INSTDIRdocsapiSample COM"
 RMDir "$INSTDIRdocsapi"
 RMDir "$INSTDIRdocs"
 RMDir "$INSTDIRcgi"
 RMDir "$INSTDIRARCHIVES"
 RMDir "$INSTDIRBACKUP"
 RMDir "$INSTDIRBADMSGS"
 RMDir "$INSTDIRCFILTER"
 RMDir "$INSTDIRDIGESTS"
 RMDir "$INSTDIRGATEWAYS"
 RMDir "$INSTDIRLOCALQLNDOMAIN"
 RMDir "$INSTDIRLOCALQ"
 RMDir "$INSTDIRLOGS"
 RMDir "$INSTDIRPUBLIC~1"
 RMDir "$INSTDIRRAWFILES"
 RMDir "$INSTDIRREMOTEQRETRY"
 RMDir "$INSTDIRREMOTEQ"
 RMDir "$INSTDIRSMTPIN"
 RMDir "$INSTDIRWCstandardHTMLhelp"
 RMDir "$INSTDIRWCstandardHTML"
 RMDir "$INSTDIRWCstandardTemp"
 RMDir "$INSTDIRWCstandardTemplates"
 RMDir "$INSTDIRWCstandard"
 RMDir "$INSTDIRwebconfigHTML"
 RMDir "$INSTDIRwebconfigTemp"
 RMDir "$INSTDIRwebconfigTemplates"
 RMDir "$INSTDIRwebconfig"
 RMDir "$INSTDIR"

; 删除 windows 注册表之中的安装路径
 DeleteRegKey HKLM SOFTWARE${ MUI_PRODUCT }

; 删除 windows 注册表的解除安装
 DeleteRegKey HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstall${ MUI_PRODUCT }"

; 删除 uninstaller 使用的语言名称 (* 进阶)
 DeleteRegvalue HKLM "Software${ MUI_PRODUCT }" "Installer Language"

;显示“完成”标头 (* 进阶)
 !insertmacro MUI_UNFINISHHEADER

SectionEnd

; 9.2 解除安装程式,初始化函式动作 (* 进阶)

Function un.onInit

;从注册表取得语言名称
 ReadRegStr $LANGUAGE HKLM "Software${ MUI_PRODUCT }" "Installer Language"

FunctionEnd

;=======================================================
; ■eof 档案结尾
3…
NSIS 确实是一个不错的安装程序制作软件。新版本 2.0a7 真正实现了中文支持和支持 WinXP 的安装对话框。不过要用它实现漂亮的安装界面和完美的安装功能就必须好好的写脚本。而 NSIS 的脚本指令是在是太多了,有时候觉得好像又回到了学习 C 语言的年代。他丰富而起强大的功能甚至可以编译出一些小而使用的软件(例如查找窗口句柄,然后…)好了,不多说。我在这里着重介绍关于 NSIS 的一些使用问题以及新界面安装程序的制作过程,中间会附上一个示例脚本。

这里只说单语言的简体中文

1. NSIS的运行问题
2.0a7 的 NSIS 安装在长文件名的目录中好像会有问题(Win2k 和 WinXP 下,Win98 下好像没问题)。你若是将NSIS 安装在类似于“E:SystemInstallerNSIS 的目录中”(其中的“Installer”是超过了 8 个字符的长文件名),运行 makensisw 或是直接在命令提示符中使用 makensis 可能会非法操作。
解决的办法是:在命令提示符下使用如下指令:

cd E:SystemInstal~1NSIS

进入 NSIS 安装目录后使用命令行:

makensis /CD /X”Compressor bzip2″ “f:1.nsi”

你可以在脚本文件中使用了“SetCompressor bzip2”指令,这样就可以去掉“/X”Compressor bzip2″”参数。使用 makensisw 的朋友我就不多说了,你可以从我上面的分析中得到点启发。自己设置一下它的运行参数吧!不过 makensisw 也有 Bug,若用 makensisw 编译时发现脚本错误,程序就立马非法操作。

2. 关于脚本的整体布局
说 NSIS 有些象高级语言的语法一点没错。NSIS 的语法非常注重先后顺序(我曾经为了一个指令的先后顺序来回 Ctrl+C & Ctrl+V 了 N 次)。例如象它的帮助文件中提到的“SetFont”指令必须在“AddBrandingImage”指令之前等。我在这里要说的而是关于如何正确的对脚本进行整体布局,使其能更加合理、高效的被使用,这样才有利于日后的安装程序更新

你可以用我后面的示例脚本为模板制作自己的 NSIS 脚本。

大体布局如下:

//////////////////////////////////////////////////////////////

预设参数(包括外部压缩器选择、编译选项、宏定义以及文件包含等)

普通安装设置

自定义函数

安装程序区域内容

安装程序回调函数及其相关函数定义

卸载程序区域内容

卸载程序回调函数及其相关函数定义

//////////////////////////////////////////////////////////////

3. 关于安装程序 Logo 画面的说明:
先前版本的 NSIS 使用 splash.exe 制作 logo画面,但若您使用 2.0a7,
我建议您使用(好像也只能使用)magiclime.exe,它在 Win2k 和 WinXP
下显示 logo画面时会有淡入淡出的效果。超 Cool….

4. 关于标牌位图(即显示在安装程序对话框中的自定义位图)的说明:
2.0a7 提供了宏指令用于在安装程序的对话框中显示标牌位图。当然你也可以用标准的 NSIS 指令。

5. 关于使用新的 Modern UI 界面的说明:
2.0a7 同样提供了宏指令使你可以使用全新的安装界面:
首先你需要安装 NSIS v2.0a7 简体中文补丁(汉化新世纪有下载):
然后打开安装目录下的 ExamplesModern UIModernUI.nsh 文件,将从
!macro MUI_INTERFACE UI ICON UNICON CHECKS PROGRESSBAR
到第一个
!macroend
之间的注释符(即分号)删除(若没有这步究省了)。保存。
接下来就可以在你自己的脚本中使用形式对话框了,并且支持 WinXP 的新的视觉效果样式。

6. 以下是我的示例脚本:

; study.nsi
 ;
 ; 以下脚本用以生成 study.exe 文件
 ; 它包含有授权协议
 ;
 ; 安装时若发现应用程序正在运行,安装程序会提示并替用户关闭应用程序
 ; 安装成功后会自动运行应用程序
 ;
 ; 启用新的 Modern UI 的宏指令语法,原先的部分指令将被禁止以防止重复
 ; 我会尽量展示如何基本指令的用法。当然宏指令也会与一些基本指令发生重
 ; 复定义的情况,不过 NSIS 照样能编译成功.
 ; 其中的一些路径、文件你可以根据需要更改

; 启用外部压缩工具(这里我们不用,原因看第 10 步)
 ;!define HAVE_UPX

; 若启用了外部压缩工具,这进行可执行文件头的压缩
 !ifdef HAVE_UPX
 ; 用 UPX 压缩可执行文件头(用其他的相关工具也行)
 !packhdr tmp.dat "E:UPXupx --best tmp.dat"
 !endif

; 编译选项

; 设置覆盖标记
 SetOverwrite on
 ; 设置压缩选项
 SetCompress auto
 ; 选择压缩方式
 SetCompressor bzip2
 ; 设置数据块优化
 SetDatablockOptimize on
 ; 设置数据写入时间
 SetDateSave on

; 相关数据预设

; 软件的名字,后面的${ NAME }调用这个名字
 !define NAME "NSIS"
 !define NAME_FULL "全方位掌握 NSIS 的使用"
 !verbose 3
 ; 包含窗口消息定义文件
 !include "${ NSISDIR }ExamplesWinMessages.nsh"
 ; 包含新界面的宏指令文件
 !include "${ NSISDIR }ExamplesModern UIModernUI.nsh"
 ; 包含标牌位图宏指令文件
 !include "${ NSISDIR }Examplesbranding.nsh"
 !verbose 4
 !define CURRENTPAGE $9
 !define TEMP1 $R0
 !define TEMP2 $R1

; 插入安装界面宏定义(这里我禁用了,启用也没问题。编译照样通过。后面我都定义过了)
 ;!insertmacro MUI_INTERFACE "modern2.exe" "adni18-installer-C-no48xp.ico" "adni18-uninstall-C-no48xp.ico" "modern.bmp" "smooth"

; 安装程序设置

; 输出文件
 OutFile "F:study.exe"
 ; 安装程序名称
 Name /LANG=2052 "NSIS"
 ; 替换对话框样式
 ChangeUI all "${ NSISDIR }ContribUIsmodern2.exe"
 ; 启用 WindowsXP 的视觉样式
 XPstyle on
 ;设置字体
 SetFont 宋体 9
 ; 标题名称
 Caption /LANG=2052 "全方位掌握 NSIS 的使用"
 ; 标牌的内容
 BrandingText /LANG=2052 /TRIMCENTER "Shao Hao"
 ; 安装程序图标
 Icon "F:12QCDIcon.ico"
 ; 安装程序显示标题
 WindowIcon on
 ; 添加标牌位图(放在顶部用 TOP 参数。后面接高度)
 AddBrandingImage LEFT 105
 ; 设定渐变背景
 BGGradient off
 ; 设置安静安装模式
 ;SilentInstall normal
 ; 设置安静卸载模式
 ;SilentUnInstall normal
 ; 执行 CRC 检查(最好用 on 打开。关闭是因为还要修改安装程序,看第 10 步)
 CRCCheck off
 ; 加载相应的语言文件
 LoadLanguageFile "${ NSISDIR }ContribLanguage filesSimpChinese.nlf"
 ; 替换各个对话框标题的文字
 SubCaption /LANG=2052 0 "授权协议"
 SubCaption /LANG=2052 1 "安装选项"
 SubCaption /LANG=2052 2 "安装目录"
 SubCaption /LANG=2052 3 "正在安装"
 SubCaption /LANG=2052 4 "完成安装"
 ; 替换默认按钮的文字
 MiscButtonText /LANG=2052 "< 上一步" "下一步 >" "取消" "关闭"
 ; 替换“安装”按钮的文字
 ;InstallButtonText /LANG=2052 "安装"
 ; 替换当文件无法被写入时的警告窗的文字
 FileErrorText /LANG=2052 "无法写入文件$r$n$0$r$n请确认文件属性不是只读且未被使用中!"

; 默认的安装目录
 InstallDir "$PROGRAMFILESQCD 3"
 ; 如果可能的化从注册表中监测安装路径
 InstallDirRegKey HKLM
 "SoftwareMicrosoftWindowsCurrentVersionUninstallNSIS"
 "UninstallString"

; 授权协议页面
 LicenseText /LANG=2052 "NSIS 是免费软件。安装前请先阅读以下协议条款" "我同意"
 ; 使用授权协议文本
 LicenseData /LANG=2052 "F:12License.txt"
 ; 设置授权协议页面的背景色
 ;LicenseBkColor 000000

; 选择要按安装的组件
 ComponentText /LANG=2052 "现在将安装 ${ NAME_FULL }到您的计算机:" "请选择安装类型" "或选择您希望安装的组件"
 InstType "完全安装(都在了)"
 InstType "典型安装"
 InstType /LANG=2052 /CUSTOMSTRING=自定义
 ;InstType /LANG=2052 /NOCUSTOM
 ;InstType /COMPONENTSONLYONCUSTOM
 CheckBitmap "${ NSISDIR }ContribIconsmodern.bmp"
 ; 替换显示磁盘空间信息的文字
 SpaceTexts /LANG=2052 "所需空间:" "可用空间:"

; 提示用户输入目录的文本
 ; DirShow hide
 ; 替换显示选择安装目录的文字
 DirText /LANG=2052 "请选择 ${ NAME } 的安装路径:" "选择目录用以安装 ${ NAME }:" "浏览..."
 ; 是否允许安装在根目录下
 AllowRootDirInstall false

; 安装信息的颜色
 ;InstallColors 000000 FFFFFF
 ; 安装进度条显示方式
 InstProgressFlags smooth colored
 ; 完成后自动关闭安装程序
 AutoCloseWindow true
 ; 显示“显示详细细节”对话框
 ShowInstDetails hide
 ; 替换“显示细节”按钮的文字
 DetailsButtonText /LANG=2052 "显示细节"
 ; 替换“完成”按钮的文字
 CompletedText /LANG=2052 "已完成"

; 卸载程序设置

; 替换卸载程序的文字
 UninstallText /LANG=2052 "现在将从你的系统中卸载 ${ NAME }:" "卸载目录:"
 ; 卸载程序图标
 UninstallIcon "F:12UnQCDIcon.ico"
 ; 替换卸载程序标题的文字
 UninstallCaption /LANG=2052 "卸载 ${ NAME }"
 ; 替换卸载程序每页按钮的文字
 UninstallSubCaption /LANG=2052 0 ":确认"
 UninstallSubCaption /LANG=2052 1 ":正在删除文件"
 UninstallSubCaption /LANG=2052 2 ":完成"
 ; 卸载程序显示方式
 ShowUninstDetails hide
 ; 替换“卸载”按钮的文字
 UninstallButtonText /LANG=2052 "卸载"

; 安装程序内容及其相关的回调函数

; 安装程序内容
 Section /e "!主要程序(必须安装)" SecCore
 SectionIn 1 2 RO
 ; 设置输出路径,每次使用都会改变
 SetOutPath $INSTDIR
 ; 循环包含目录下全部内容
 File /r "F:121*.*"
 ; 只包含一个文件
 File "F:12gf.gif"
 ; 处理动态连接库文件
 IfFileExists "$INSTDIR1.dll" 0 NoFile1
 ; 取消组件注册
 UnRegDll "$INSTDIR1.dll"
 Delete "$INSTDIR1.dll"
 NoFile1:
 File "F:121.dll"
 ; 注册组件
 RegDLL "$INSTDIRQCDIconMgr.dll"
 ; 写入软件注册键值
 WriteRegStr HKLM "SoftwareNSISNSIS" "" "$INSTDIR"
 ; 为 Windows 卸载程序写入键值
 WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstallNSIS" "DisplayName" "NSIS(只用于移除)"
 WriteRegStr HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstallNSIS" "UninstallString" '"$INSTDIRuninst.exe"'
 ; 写入生成卸载程序(可多次使用)
 WriteUninstaller "$INSTDIRuninst.exe"
 SectionEnd

Section /e "扩展(可选)" SecUpdate
 SectionIn 1
 SetOutPath "$INSTDIRPlugins"
 File "F:12qcdplusPlugins2.dll"
 File /r "F:12qcdplusPlugins3"
 SectionEnd

SubSection /e "选项" SecOptions
 Section /e "创建桌面图标" SecDesktopShortCut
 SectionIn 1 2
 SetOutPath "$INSTDIRPlugins"
 CreateShortCut "$DESKTOPNSIS.lnk" "$INSTDIRgf.gif"
 WriteUninstaller "$INSTDIRuninst.exe"
 SectionEnd

Section /e "创建快速启动栏图标" SecQuickbarShortCut
 SectionIn 1 2
 SetOutPath "$INSTDIRPlugins"
 CreateShortCut "$QUICKLAUNCHNSIS.lnk" "$INSTDIRgf.gif"
 WriteUninstaller "$INSTDIRuninst.exe"
 SectionEnd

Section /e "创建 NSIS 程序组" SecShortCutGroup
 SectionIn 1 2
 SetOutPath "$INSTDIRPlugins"
 CreateDirectory "$SMPROGRAMSNSIS"
 CreateShortCut "$SMPROGRAMSQCD Playergf.lnk" "$INSTDIRgf.gif"
 WriteUninstaller "$INSTDIRuninst.exe"
 SectionEnd
 SubSectionEnd

Section "-Run"; 运行
 ExecWait '"$INSTDIR2.exe" -p'
 WriteINIStr "$INSTDIRNSIS.ini" "2" "Language" "Chinese"
 Exec "$INSTDIR2.exe"
 ExecShell open "$INSTDIR汉化说明.htm"
 SectionEnd

Section ""
 ;Invisible section to display the Finish header
 !insertmacro MUI_FINISHHEADER SetHeader
 SectionEnd

; 安装程序初始化设置
 Function .onInit
 ; 发现有程序正在运行时提示退出后继续运行
 loop:
 FindWindow $R0 "NSIS"
 IntCmp $R0 0 done
 MessageBox MB_OKCANCEL
 "NSIS 正在运行。$n点击“确定”按钮会关闭 NSIS 然后继续安装,点击“取消”按钮将退出安装程序"
 IDOK NoAbort
 Abort
 NoAbort:
 SendMessage $R0 1029 0 0
 ; 稍等后继续检测直至检测不到或用户选择取消
 Sleep 444
 Goto loop
 done:
 ; 显示 Logo 画面
 SetOutPath $TEMP
 File /oname=qcd_logo.bmp "F:12logo.bmp"
 File /oname=magiclime.exe "${ NSISDIR }Binmagiclime.exe"
 ExecWait '"$TEMPmagiclime.exe" qcd_logo.bmp'
 Delete "$TEMPmagiclime.exe"
 Delete "$TEMPqcd_logo.bmp"
 ; 初始化标牌位图
 !insertmacro BI_INIT $R0
 FunctionEnd

; 安装程序主界面的相关文本
 Function SetHeader
 !insertmacro MUI_HEADER_INIT
 !insertmacro MUI_HEADER_START 1
 !insertmacro MUI_HEADER_TEXT 2052 "授权协议" "安装 ${ NAME } 之前,请认真阅读一下授权条款."
 !insertmacro MUI_HEADER_STOP 1
 !insertmacro MUI_HEADER_START 2
 !insertmacro MUI_HEADER_TEXT 2052 "选择组件" "请选择你要安装的组件."
 !insertmacro MUI_HEADER_STOP 2
 !insertmacro MUI_HEADER_START 3
 !insertmacro MUI_HEADER_TEXT 2052 "选择安装位置" "为 ${ NAME } 选择一个安装目录."
 !insertmacro MUI_HEADER_STOP 3
 !insertmacro MUI_HEADER_START 4
 !insertmacro MUI_HEADER_TEXT 2052 "安装" "${ NAME } 已经安装,请等待."
 !insertmacro MUI_HEADER_STOP 4
 !insertmacro MUI_HEADER_START 5
 !insertmacro MUI_HEADER_TEXT 2052 "完成" "安装顺利完成."
 !insertmacro MUI_HEADER_STOP 5
 !insertmacro MUI_HEADER_END
 FunctionEnd

; 初始化安装程序对话框的显示文本
 Function .onInitDialog
 !insertmacro MUI_INNERDIALOG_INIT
 !insertmacro MUI_INNERDIALOG_START 1
 !insertmacro MUI_INNERDIALOG_TEXT 2052 1040 "如果你同意协议中的所有条款,选择“我同样”继续安装,如果你选择了“取消”,安装程序将终止,只有接受条款才能安装 ${ NAME }."
 !insertmacro MUI_INNERDIALOG_STOP 1
 !insertmacro MUI_INNERDIALOG_START 2
 !insertmacro MUI_INNERDIALOG_TEXT 2052 1042 "描述"
 !insertmacro MUI_INNERDIALOG_TEXT 2052 1043 "移动你的鼠标到组件上,便可以见到相关的描述."
 !insertmacro MUI_INNERDIALOG_STOP 2
 !insertmacro MUI_INNERDIALOG_START 3
 !insertmacro MUI_INNERDIALOG_TEXT 2052 1041 "目标文件夹"
 !insertmacro MUI_INNERDIALOG_STOP 3
 !insertmacro MUI_INNERDIALOG_END
 FunctionEnd

; 转到下页面时的处理
 Function .onNextPage
 !insertmacro MUI_NEXTPAGE_OUTER
 !insertmacro MUI_NEXTPAGE SetHeader
 ; 标牌位图设置
 !insertmacro BI_NEXT
 FunctionEnd

; 转到上一页面时的处理
 Function .onPrevPage
 !insertmacro MUI_PREVPAGE
 ; 标牌位图设置
 !insertmacro BI_PREV
 FunctionEnd

; 鼠标移到指定组件时的显示处理
 Function .onMouseOverSection
 ; 用宏指令设置安装自己的注释文本
 !insertmacro MUI_DESCRIPTION_INIT
 !insertmacro MUI_DESCRIPTION_TEXT 2052 ${ SecCore } "安装主应用程序"
 !insertmacro MUI_DESCRIPTION_TEXT 2052 ${ SecUpdate } "安装 NSIS 的更新或新增插件"
 !insertmacro MUI_DESCRIPTION_TEXT 2052 ${ SecOptions } "选择 NSIS 的其他安装选项,例如:创建快捷方式和应用程序组的"
 !insertmacro MUI_DESCRIPTION_TEXT 2052 ${ SecDesktopShortCut } "在用户的桌面上创建 NSIS 的快捷方式"
 !insertmacro MUI_DESCRIPTION_TEXT 2052 ${ SecQuickbarShortCut } "在用户的快速栏里创建 NSIS 快捷方式"
 !insertmacro MUI_DESCRIPTION_TEXT 2052 ${ SecShortCutGroup } "在用户的开始菜单的程序中创建 NSIS 的快捷方式组"
 !insertmacro MUI_DESCRIPTION_END
 FunctionEnd

; 安装成功完成后的设置
 Function .onInstSuccess
 ; 自己加吧:)
 FunctionEnd

; 放弃安装时警告窗口的显示信息
 Function .onUserAbort
 !insertmacro MUI_ABORTWARNING 2052 "你确定要推出 ${ NAME } 的安装吗?"
 !insertmacro MUI_ABORTWARNING_END
 FunctionEnd

; 安装程序标牌位图列表
 !insertmacro BI_LIST
 !insertmacro BI_LIST_ADD "F:12Brand1.bmp" /RESIZETOFIT
 !insertmacro BI_LIST_ADD "F:12Brand2.bmp" /RESIZETOFIT
 !insertmacro BI_LIST_ADD "F:12Brand3.bmp" /RESIZETOFIT
 !insertmacro BI_LIST_END

; 卸载程序及其相关回调函数

; 卸载程序内容
 Section "Uninstall"
 ExecWait '"$INSTDIR12.exe" /un'
 ; 循环删除文件直至删除文件夹
 RMDir /r "$SMPROGRAMSNSIS"
 UnRegDLL "$INSTDIR2.dll"
 Delete "$INSTDIRQCDIconMgr.dll"
 Delete "$INSTDIR*.*"
 DeleteRegKey HKLM "SoftwareNSIS"
 DeleteRegKey HKLM "SoftwareMicrosoftWindowsCurrentVersionUninstallNSIS"
 MessageBox MB_YESNO&line;MB_ICONQUESTION
 "是否连插件目录也要删除?$n(若您要保留这些文件,请点击下面的“否”按钮)"
 IDNO NoDelete
 ; 全删光!!!
 RMDir /r "$INSTDIR"
 NoDelete:
 SectionEnd

; 定义卸载程序标牌位图
 !define BI_UNINSTALL
 !include "${ NSISDIR }Examplesbranding.nsh"

; 初始化卸载程序对话框的设置
 Function un.onInit
 ; 初始化标牌位图
 !insertmacro BI_INIT $R0
 FunctionEnd

; 卸载程序主界面的相关文本
 Function un.SetHeader
 !insertmacro MUI_HEADER_INIT
 !insertmacro MUI_HEADER_START 1
 !insertmacro MUI_HEADER_TEXT 2052 "卸载 ${ NAME }" "${ NAME_FULL } 将从你的系统里移除."
 !insertmacro MUI_HEADER_STOP 1
 !insertmacro MUI_HEADER_START 2
 !insertmacro MUI_HEADER_TEXT 2052 "卸载" "${ NAME } 正在被卸载,请等待."
 !insertmacro MUI_HEADER_STOP 2
 !insertmacro MUI_HEADER_START 3
 !insertmacro MUI_HEADER_TEXT 2052 "完成" "${ NAME_FULL } 已从你的系统中移除."
 !insertmacro MUI_HEADER_STOP 3
 !insertmacro MUI_HEADER_END
 FunctionEnd

; 初始化卸载程序对话框时的处理
 Function un.onInitDialog
 ; 自己加:)
 FunctionEnd

; 卸载程序转到下一页时的处理
 Function un.onNextPage
 !insertmacro MUI_NEXTPAGE_OUTER
 !insertmacro MUI_NEXTPAGE un.SetHeader
 ; 标牌位图设置
 !insertmacro BI_NEXT
 FunctionEnd

; 卸载程序完成时的处理
 Function un.onUninstSuccess
 ; 再加^_^
 FunctionEnd

; 放弃卸载程序时的处理
 Function un.onUserAbort
 FunctionEnd

; 卸载程序标牌位图列表
 !insertmacro BI_LIST
 !insertmacro BI_LIST_ADD "F:12UnBrand1.bmp" /RESIZETOFIT
 !insertmacro BI_LIST_ADD "F:12UnBrand2.bmp" /RESIZETOFIT
 !insertmacro BI_LIST_END

7.注意两个标牌位图列表宏指令必须放置到函数的后面,就像我编写的那样,安装程序的标牌位
图列表放置到到所有安装程序回调函数的后面;卸载程序的标牌位图列表放置到所有卸载程序回
调函数的后面。
关键是要注意整体布局(这样才不会看迷糊),当然若编译不能通过,就要进行合适的调整。

8. 使用指令:

makensis /CD f:study.nsi”

进行编译(这个示例脚本我没试过-_-,若编译无法通过只好自己动手研究研究了。我放出来只是用于参考学习)

9. 还没完—— NSIS 对多语言的支持还是不够完美(注意:是完美——有点鸡蛋里挑骨头的味道)。
我们继续:
用 ExeScope (其他的资源编辑器也行,别说你没有)打开生成的 study.exe 文件,找到对话框资源的 105 项目。你会发现有一块很明显的用以显示标牌位图的控件。因为 NSIS 在插件此控件时是使其保持于授权协议文件控件等高的,所以当使用用 modern2.exe 对话框模板时会与对话框顶部多出 45 的距离——不罗嗦了,总之根据需要搞得好看就行(别过火!)。顺便提一下,NSIS 安装目中的 ContribUIs 子目录下提供了多种对话框模板,你可以自行选用(就是用
ChangeUI 指令或用宏指令),甚至根据需要用资源编辑器修改模板资源。

接着修改:找到 111 项,把里面的资源汉化掉(新的界面居然没有对此对话框的多语言支持!这个对话框只有在大容量的安装程序执行时才显示)。

用 ExeScope 更新后,再用 Hex WorkShop 打开我问可怜的试验品——study.exe,查找字符串“unpacking data: ”(没引号的,你别瞎加。找不到别怪我 $_$)。找到后处理一下,用“正在解压缩数据:”
替换(正好 8 个字符 Yeah!)。

10. 现在我要说为什么不启用 UPX 压缩以及关闭 CRC 检测:
用 UPX 压缩可执行文件头或启用 CRC 检测(起用的话就是打开了自校验),我们的 ExeScope 就无用武之地了(除非你够牛会动态跟踪技术和反汇编把文件头壳和自校验去掉,否则还是关掉的好)。

至此全部完工。

4…
创建 NSIS 脚本的习惯: 创建一个 include 目录,用来保存安装用的文件, .nsi 文件放在 include 的上级目录,再在 include 目录里创建一个 resource,用来保存一些资源文件比如图标、界面位图、自己修改的 UI 等等。

然后分析一下官方的安装程序,嗯嗯,先清空临时文件夹,这是为了为了找东西方便,然后启动安装程序,再到临时目录里找一个 nxxx.tmp 这样的目录,里面有一些释放出来的资源 gaydata.ini、modern-header.bmp、classic256.bmp、modern256.bmp、opt2page.ini、 opt3page.ini。那几个位图一看就明白,不用解释,gaydata.ini 呢,里面有从 sec0 到 sec47 的定义,所以我们可以确定一共有 47 个区段,而且区段的名称是根据 gaydata.ini 来确定的,如何知道是根据 gaydata.ini 来确定的 的呢,你在安装程序刚启动的时候(刚显示许可页面的时候)找到临时的那个目录(也就是 NSIS 里的 $PLUGINSDIR 目录),把一个区段名称改一下,比如把“Winamp (required)”改为 aaa,等进入组件选择页面的时候第一个就是 aaa 了,而如果把“Winamp (required)”清空的话,第一个区段就不见了。 opt2page.ini、opt3page.ini 分别是最后两个页面用来选择连接方式和外观的。分析后就可以动手了……

1.建立基本的结构

首先在脚本头部定义一些版本号等值,比如

!define VERSION "5.05"
 !define VERSION_NUM "505"

这样版本号变的时候在脚本头部改一下就行了,不用在脚本的每个地方都改
然后定义输出文件名,为了方便 full、pro、lite 三个版本切换方便。

!define FILE_NAME "Winamp${ VERSION_NUM }_full"

有关定义的说明可以看这里

再下来就是安装程序属性的设置了,必须的设置有

Name "Winamp"
 OutFile "${ FILE_NAME }.exe"

当然

SetCompressor lzma

应该也是必须的,LZMA 不止压缩率大很多,而且不太准确的一个属性是启动快不少,然后再设置一个区段就构成了主体部分,已经能够编译了

Section "主程序"
 SectionEnd

2. 插入页面

首先要

!include "MUI.nsh"

这样才能使用 NSIS 提供的一些宏来插入页面,要插入的页面是

!insertmacro MUI_PAGE_LICENSE ".resourceLicense.txt"
 !insertmacro MUI_PAGE_COMPONENTS
 !insertmacro MUI_PAGE_DIRECTORY
 !insertmacro MUI_PAGE_COMPONENTS
 !insertmacro MUI_PAGE_INSTFILES
 !insertmacro MUI_UNPAGE_CONFIRM
 !insertmacro MUI_UNPAGE_INSTFILES

最后还要插入语言

!insertmacro MUI_LANGUAGE English

3. 完善安装程序属性设置
当然安装程序的属性还要增加一些设置

BrandingText "Nullsoft Install System -- built on ${ __DATE__ } at ${ __TIME__ }"

这是设置安装程序个人标志的

InstallDir "$PROGRAMFILESWinamp"

设置一个默认的安装路径

InstallDirRegKey HKCU "SoftwareWinamp" ""

优先读取注册表里保存的路径,如果存在就是用注册表保存的路径

4. 设置页面
图标的定义

!define MUI_ICON ".resourceinst.ico"
 !define MUI_UNICON ".resourceuninst.ico"

定义了安装程序图标和卸载程序图标

!define MUI_HEADERIMAGE

定义在安装程序顶端显示一个位图

!define MUI_HEADERIMAGE_BITMAP ".resourcemodern-header.bmp"

定义要显示的位图,必须是本地机器上的

!define MUI_COMPONENTSPAGE_NODESC

指定组件选择页面不使用描述区域

5. 设置页面文本

!define MUI_LICENSEPAGE_TEXT_TOP "Please read and agree to the license terms below before installing."

指定许可页面上顶端显示的文本

!define MUI_COMPONENTSPAGE_TEXT_TOP "This will install Winamp ${ VERSION }. This installer contains the full install."

指定组件选择页面顶端的文本

!define MUI_DIRECTORYPAGE_TEXT_TOP "Setup has determined the optimal location to install. If you would like to change the folder, do so now."

指定目录选择页面的文本

!define MUI_ABORTWARNING

定义按取消按钮时,提示是否真的退出

6. 设定安装类型,并把补全所有的区段

InstType "Full"
 InstType "Standard"
 InstType "Lite"
 InstType "Minimal"

一共四个安装类型,还有一个 Custom 类型系统会自动添加,不必干预

然后在创建 46 个区段,一共有 47 个,名称可以随便起,因为区段的名称到后面会由 gaydata.ini 来从命名,比如

Section " "
 SectionEnd

7. .onInit 函数

这个函数是在安装程序 GUI 启动完毕的时候开始执行里面的代码,应该把那些资源文件在这个阶段释放到用户电脑以供使用

InitPluginsDir

初始化 $PLUGINSDIR 也就是插件目录

File "/oname=$PLUGINSDIRgaydata.ini" ".resourcegaydata.ini"
 File "/oname=$PLUGINSDIRopt2page.ini" ".resourceopt2page.ini"
 File "/oname=$PLUGINSDIRopt3page.ini" ".resourceopt3page.ini"
 File "/oname=$PLUGINSDIRclassic256.bmp" ".resourceclassic256.bmp"
 File "/oname=$PLUGINSDIRmodern256.bmp" ".resourcemodern256.bmp"

因为在 .onInit 里使用 File 会使程序启动时要搜索很久,所以还应该使用 ReserveFile,ReserveFile 的说明看这里。
在 !include “MUI.nsh” 上面增加

ReserveFile ".resourcegaydata.ini"
 ReserveFile ".resourceopt2page.ini"
 ReserveFile ".resourceopt3page.ini"
 ReserveFile ".resourceclassic256.bmp"
 ReserveFile ".resourcemodern256.bmp"
 ReserveFile "${ NSISDIR }PluginsInstallOptions.dll"

因为 InstallOptions.dll 在自定义界面要使用,所以也要加入

8. 组件的隐藏和显示
细心的朋友都看到了脚本里面有两个 !insertmacro MUI_PAGE_COMPONENTS,那么组件选择页面就会出现两次,察看 gaydata.ini 就知道第一次显示的是 sec0 到 sec36,第二次显示的是 sec37 到 sec 47。
关于页面的说明请看这里
每个页面都有三个函数: Pre、Show、Leave,分别是预载入、显示、离开,在 MUI 界面可以用定义的方法来插入函数,比如在 !insertmacro MUI_PAGE_COMPONENTS 前(插入上一个页面之后) 定义一个 MUI_PAGE_CUSTOMFUNCTION_PRE 函数就可以插入一个预载入函数。在本次脚本中在第一个组件选择页面作如下定义

!define MUI_PAGE_CUSTOMFUNCTION_PRE ComponentPre
 !define MUI_PAGE_CUSTOMFUNCTION_SHOW ComponentShow

上面定义了 ComponentPre、ComponentShow 函数,当然定义的函数名可以随便起,但一般来说名字都要表达它的含义,便于阅读。

在开始创建这两个函数之前还要定义一些内容

!define SECTION_COMPONENT_END 36
 !define SECTION_ASSCOIATION_START 37
 !define SECTION_TOTAL 47

上面定义了 36 是要安装的组件最后的区段索引好,37 是文件关联等的开始区段索引号,47 是总共的区段数。ComponentPre 函数的内容如下

Function ComponentPre
 Push $0
 Push $1

Call SectionTextReset

StrCpy $1 0
 loop:
 ReadINIStr $0 "$PLUGINSDIRgaydata.ini" "secnames" "sec$1"
 StrCmp $0 "" 0 +2
 SectionSetText $1 ""
 StrCmp $1 ${ SECTION_COMPONENT_END } loop_quit
 IntOp $1 $1 + 1
 Goto loop
 loop_quit:

StrCpy $1 ${ SECTION_ASSCOIATION_START }
 SectionSetText $1 ""
 StrCmp $1 ${ SECTION_TOTAL } +3
 IntOp $1 $1 + 1
 Goto -3

Pop $1
 Pop $0
 FunctionEnd

这个函数调用了 SectionTextReset 函数,SectionTextReset 函数如下

Function SectionTextReset
 Push $R0

StrCpy $R0 0
 SectionSetText $R0 " "
 StrCmp $R0 ${ SECTION_TOTAL } +3
 IntOp $R0 $R0 + 1
 Goto -3

Pop $R0
 FunctionEnd

SectionTextReset 函数构成一个循环,$R0 从 0 开始递增,直到等于 ${ SECTION_TOTAL } 后跳出循环,这个循环把所有区段的名称都重置为空格,在两个 MUI_PAGE_COMPONENTS 页面的预载入函数都调用一次。这是因为 Show 函数会把一些区段隐藏,即把区段名称设为空值,在下一个 MUI_PAGE_COMPONENTS 页面的 Pre 阶段必须给它一个名称,否则它将一直隐藏。

调用了 SectionTextReset 函数之后是一个循环,这个循环读取 “$PLUGINSDIRgaydata.ini” 的 sec0 到 ${ SECTION_COMPONENT_END } ,如果某个 sec 读到的值为空,则把该区段隐藏,也就是把区段名设为空值。你可以试试英文原版,刚启动时把 “$PLUGINSDIRgaydata.ini” 的 sec0 设为空值,到了组件选择页面 Winamp (required) 区段就被隐藏了。

再下来也是一个循环,把 ${ SECTION_ASSCOIATION_START } 到 ${ SECTION_TOTAL } 的区段隐藏,因为第一个 MUI_PAGE_COMPONENTS 只需要显示 0 到 ${ SECTION_COMPONENT_END } 的区段。ComponentShow 函数如下

Function ComponentShow
 Push $0
 Push $1

StrCpy $1 0
 loop:
 ReadINIStr $0 "$PLUGINSDIRgaydata.ini" "secnames" "sec$1"
 SectionSetText $1 $0
 StrCmp $1 ${ SECTION_COMPONENT_END } loop_quit
 IntOp $1 $1 + 1
 Goto loop
 loop_quit:

Pop $1
 Pop $0
 FunctionEnd

也是一个循环,$1 的值从 0 到 ${ SECTION_COMPONENT_END } 递增,则是依次从 sec0 到 sec36 读取 gaydata.ini 相应的值,并根据读取道的值来从命名区段名称。

第二个组件页面对应的 AsscoiationPre、AsscoiationShow 与上面的基本一致,只是要隐藏的区段索引不同而已。

9. 隐藏控件
组件页面第二次显示的时候,有几个控件是隐藏的,用 Resource Hacker 打开 ${ NSISDIR }ContribUIsmodern.exe 里面的 104 对话框就是组件显示页面,要隐藏的空间 ID 为 1017 (显示安装类型) 和 1021 (它左边显示的文本) 还有 1023 (磁盘空间显示的控件)。显示和隐藏控件的指令为 ShowWindow ,说明请看这里。
隐藏控件的代码需要加在 AsscoiationShow 函数里。

FindWindow $0 "#32770" "" $HWNDPARENT

;获取一个窗口句柄保存在 $0 里

GetDlgItem $1 $0 1017

;获取 1017 控件的句柄

ShowWindow $1 ${ SW_HIDE }

;隐藏 1017 控件,其他几个控件的隐藏指令依次为

GetDlgItem $1 $0 1021
 ShowWindow $1 ${ SW_HIDE }
 GetDlgItem $1 $0 1023
 ShowWindow $1 ${ SW_HIDE }

除了控件隐藏之外,还有两处文本需要更改,由于使用 !define 只能对第一次显示的组件页面更改,所以第二次显示的文本只能自己用 SendMessage 来改了

GetDlgItem $1 $0 1006
 SendMessage $1 ${ WM_SETTEXT } 0 "STR:Select which icons you want installed, and whether you want files and CDs associated with"
 GetDlgItem $1 $0 1022
 SendMessage $1 ${ WM_SETTEXT } 0 "STR:Select icons to install and media associations:"

组件A,组件B,A依赖于B。意思就是组件B可单独安装,但是B被安装时,A必须要被安装

实现起来的脚本:
——————————————————————————–

Function .onSelChange
 SectionGetFlags ${ secA } $1
 StrCmp $1 1 0 +2
 SectionSetFlags ${ secB } 1
 FunctionEnd

将此代码复制到最后一个Sectiongroupend(如果没有就是Sectionend)的后面即可。其中SecA、SecB分别改为你要定义的互斥软件的编号

案例分析:
QQ狂人DIY版的2005.10.29组件包里面,Tim平台是很重要的组件,很多组件依赖他才可以运行,看看狂人是怎么处理的(Tim平台编号是Sec18)
——————————————————————————–

Function .onSelChange
 SectionGetFlags ${ sec11 } $2
 StrCmp $2 1 0 +2
 SectionSetFlags ${ sec18 } 1
 FunctionEnd

——————————————————————————–

组件A,组件B,两组件互斥。意思就是组件A与B不能同时安装,选择组建A时,组件B自动被取消选择,而当B被勾选时,A被自动取消选择。

实现起来的脚本:
——————————————————————————–

Function .onSelChange
 SectionGetFlags ${ secA } $1
 SectionGetFlags ${ secB } $2
 StrCmp $2 1 0 +2
 SectionSetFlags ${ secA } 0
 StrCmp $1 1 0 +2
 SectionSetFlags ${ secB } 0
 FunctionEnd

将此代码复制到最后一个Sectiongroupend(如果没有就是Sectionend)的后面即可。其中SecA、SecB分别改为你要定义的互斥软件的编号

案例分析:
QQ狂人DIY版的2005.10.29组件包里面有2个不同的外挂,狂人将其分别定义为Sec03和SecWG3,我们想让4.x外挂被选时3.x自动被取消,可以使用这样的代码
——————————————————————————–

Function .onSelChange
 SectionGetFlags ${ sec03 } $1
 SectionGetFlags ${ secWG3 } $2
 StrCmp $2 1 0 +2
 SectionSetFlags ${ sec03 } 0
 StrCmp $1 1 0 +2
 SectionSetFlags ${ secWG3 } 0
 FunctionEnd

因为狂人组件包里面已经有Function .onSelChange以及FunctionEnd,因此我们只需要把
——————————————————————————–

SectionGetFlags ${ sec03 } $1
 SectionGetFlags ${ secWG3 } $2
 StrCmp $2 1 0 +2
 SectionSetFlags ${ sec03 } 0
 StrCmp $1 1 0 +2
 SectionSetFlags ${ secWG3 } 0

添加到Function .onSelChange以及FunctionEnd之间,最好在FunctionEnd的上一行,就OK了

THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容