八、卸载/回滚安装
安装过程中遇到问题不能再继续完成安装,那么我们应该把机器环境恢复到安装前的状态,这就需要回滚安装,每个安装程序都有它自己的安装步骤和组件,所以要根据实际情况来设置标识来标注安装了哪些组件或操作,在回滚时才可以依此来恢复,另外我们的回滚程序要写在哪里才会被正确的调用;还有卸载安装包括哪些页面,需要怎么做等等;开始之前我们先介绍下回调函数。
回调函数
一般函数名以 . 开头的作为回调函数保留,这些函数将会由安装/卸载程序在安装/卸载时需要某些用途时调用。一些主要的安装/卸载回调函数如下:
-
.onGUIInit/un.onGUIInit:该回调将会在第一个页面被载入并且显示安装/卸载程序对话框前被调用,允许你来调整用户界面。
-
.onInit/un.onInit:该回调将会在当安装/卸载程序接近完成初始化时调用。如果 .onInit/ un.onInit 函数调用了Abort,则安装/卸载程序立即退出。
-
.onInstFailed/un.onUninstFailed:该回调函数当在安装/卸载失败后用户点击“取消”按钮时被调用。
-
.onInstSuccess/un.onUninstSuccess:该回调当安装/卸载成功且正当安装/卸载窗口关闭前(如果自动关闭被设为 false 时可能在用户点击“关闭”之后)调用.
-
.onGUIEnd/un.onGUIEnd:该回调正当安装/卸载程序窗口关闭之后被调用。需要时用来释放任何与用户界面有关的插件。
安装回滚
考虑到在安装失败时只恢复已经安装的组件或操作,所以需要根据实际情况来标识安装了哪些组件或操作。这里我们使用注册表来保存这些标识,并在回滚中判断标识要执行相应的指令。
以下示例保存数据库是否创建的标识"DBCreated",写入到注册表"Software\Microsoft\Windows\CurrentVersion\App Paths\ADWebManager.exe":
!define DB_Created_RegKey "DBCreated"
!define PRODUCT_NAME "ADWebManager"
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\${PRODUCT_NAME}.exe" Section "数据库服务器配置" SEC04" ; 省略”连接SQL Server数据库、查询数据库是否存在“代码 ${If} $0 == 0 ; 数据库未创建 ; 省略”创建数据库和初始化数据库“代码 WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "${DB_Created_RegKey}" "True" ${Else} WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "${DB_Created_RegKey}" "False" ${EndIf} ; 省略代码 SectionEnd
当注册表项标识"DBCreated"值为“True"时,回滚时要删除创建的数据库,示例如下所示:
Function Rollback ; 省略代码 ; 恢复数据库设置 DetailPrint "恢复数据库设置" ReadRegStr $0 HKLM "${PRODUCT_DIR_REGKEY}" "${DB_Created_RegKey}" ${If} $0 == True ; 删除数据库 ; 省略“删除数据库”代码 ${EndIf}
; 省略代码
FunctionEnd
安装过程中,当安装或配置组件信息时出错则使用指令Abort取消安装,停止执行脚本,这时我们可以在回调函数.onInstFailed执行回滚操作,如下示例:
Function .onInstFailed
Call Rollback
FunctionEnd
安装卸载
安装程序安装成功完成后,需要生成卸载程序并生成相应的快捷方式,示例如下:
Section -Post ; 写入卸载程序 WriteUninstaller "$INSTDIR\ADWebManager\Uninstall.exe" ; 创建桌面快捷方式文件夹 CreateDirectory "$DESKTOP\${PRODUCT_NAME}" CreateShortCut "$DESKTOP\${PRODUCT_NAME}\Website.lnk" "https://www.baidu.com" CreateShortCut "$DESKTOP\${PRODUCT_NAME}\Uninstall.lnk" "$INSTDIR\ADWebManager\Uninstall.exe" ; 省略其他代码 SectionEnd
NSIS内置的卸载页面有以下(参见NSIS用户手册):
UninstPage uninstConfirm
UninstPage instfiles
现代用户界面有以下内置页面(参见NSIS Modern User Interface):
MUI_UNPAGE_WELCOME
MUI_UNPAGE_CONFIRM
MUI_UNPAGE_LICENSE textfile
MUI_UNPAGE_COMPONENTS
MUI_UNPAGE_DIRECTORY
MUI_UNPAGE_INSTFILES
MUI_UNPAGE_FINISH
以下是我们的卸载程序页面示例:
; 卸载程序页面开始---------------------- ; 欢迎页面 !insertmacro MUI_UNPAGE_WELCOME ; 卸载安装记录页面 !insertmacro MUI_UNPAGE_INSTFILES ; 完成页面 !insertmacro MUI_UNPAGE_FINISH ; 卸载程序页面结束----------------------
在卸载页面开始前我们希望有提示,让用户确认一下是否卸载,如下:
Function un.onInit MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "你确实要完全移除 $(^Name) ,其及所有的组件?" IDYES + 2 Abort FunctionEnd
或者也可以在插入确认页面:
!insertmacro MUI_UNPAGE_CONFIRM
我们知道如果一个区段名为“Uninstall”或以“un.”为前缀,那么它就是一个卸载程序区段;函数名以 un. 开头的函数将会被创建在卸载程序里,因此,普通安装区段和函数不能调用卸载函数,而卸载区段和卸载函数也不能调用普通安装程序的函数。所以我们在卸载程序区段“Uninstall”调用卸载程序,由于卸载和安装失败回滚的代码类似,我们可以只写一份,但卸载区段调用的函数必须以“un.”为前缀,所以我们可以改进一下Rollback函数,使它既可以被安装程序调用也可以被卸载程序调用,如下:
Section Uninstall
Call un.Rollback
SectionEnd
!macro MYMACRO un
Function ${un}Rollback
; 省略代码
; 恢复数据库设置 DetailPrint "恢复数据库设置" ReadRegStr $0 HKLM "${PRODUCT_DIR_REGKEY}" "${DB_Created_RegKey}" ${If} $0 == True ; 删除数据库 ; 省略“删除数据库”代码 ${EndIf}
; 省略代码
FunctionEnd
!macroend
!insertmacro MYMACRO ""
!insertmacro MYMACRO "un."
卸载成功完成后我们希望有提示信息,可以在回调函数中提示,如下:
Function un.onUninstSuccess HideWindow MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) 已成功地从你的计算机移除。" FunctionEnd
至此,NSIS安装程序的制作所有章节已经介绍完毕。