反编译 NSIS 的脚本(二)

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

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

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

InstallDir "$PROGRAMFILES\Winamp"

设置一个默认的安装路径

InstallDirRegKey HKCU "Software\Winamp" ""

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

4. 设置页面
图标的定义

!define MUI_ICON ".\resource\inst.ico"
 !define MUI_UNICON ".\resource\uninst.ico"

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

!define MUI_HEADERIMAGE

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

!define MUI_HEADERIMAGE_BITMAP ".\resource\modern-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=$PLUGINSDIR\gaydata.ini" ".\resource\gaydata.ini"
 File "/oname=$PLUGINSDIR\opt2page.ini" ".\resource\opt2page.ini"
 File "/oname=$PLUGINSDIR\opt3page.ini" ".\resource\opt3page.ini"
 File "/oname=$PLUGINSDIR\classic256.bmp" ".\resource\classic256.bmp"
 File "/oname=$PLUGINSDIR\modern256.bmp" ".\resource\modern256.bmp"

因为在 .onInit 里使用 File 会使程序启动时要搜索很久,所以还应该使用 ReserveFile,ReserveFile 的说明看这里。

在 !include “MUI.nsh” 上面增加

ReserveFile ".\resource\gaydata.ini"
 ReserveFile ".\resource\opt2page.ini"
 ReserveFile ".\resource\opt3page.ini"
 ReserveFile ".\resource\classic256.bmp"
 ReserveFile ".\resource\modern256.bmp"
 ReserveFile "${NSISDIR}\Plugins\InstallOptions.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 "$PLUGINSDIR\gaydata.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 函数之后是一个循环,这个循环读取 “$PLUGINSDIR\gaydata.ini” 的 sec0 到 ${SECTION_COMPONENT_END} ,如果某个 sec 读到的值为空,则把该区段隐藏,也就是把区段名设为空值。你可以试试英文原版,刚启动时把 “$PLUGINSDIR\gaydata.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 "$PLUGINSDIR\gaydata.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}\Contrib\UIs\modern.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:"
© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容