在NSIS中实现安装时取消并回滚(2)——写个线程控制插件实现它

需要自己完成这个功能。下面就来介绍一种相对比较简单的实现方式。

功能难点在上一篇中已经有描述,这里就不赘述了。基本原理就是在安装过程中,使用插件中把后台的释放文件的线程挂起,问用户是不是真要取消安装,是的话就使用安装过程中生成的Log文件进行回滚,不是的话就让被挂起的线程继续运行。

大致要解释如下几个问题

1. 编写线程控制插件。

2. 开启安装页面的取消按钮,并自定义取消函数。

3. 与安装日志功能集成,实现回滚操作。

首先是实现线程控制插件。这个插件有如下几个功能:

1. 挂起后台所有线程

2. 继续后台所有线程

3. 终止后台所有线程

为什么是后台?因为UI线程要用来弹出一个MessageBox让用户点,如果也挂起了,程序就死了。为什么是所有线程呢?因为如果有多个后台线程,如果那个线程不主动配合调查,想要找出哪个线程是用来释放文件似乎不是很容易。简单起见,就全挂起得了,好在不会有什么问题。更好的是,这个安装程序在安装页面只有两个线程,一个UI线程,一个后台释放文件。

还有一点要注意的就是TerminateThread函数是异步的。最好多调用一个WaitForSingleObject来保证函数返回时,线程的确已经被和谐了才行。(不调似乎也没有什么问题)

代码就不贴了,可以从这里下载。关于如何编写NSIS插件可以参考制作NSIS命令行窗口输出插件这篇文章。

然后就是开启安装页面的取消按钮,在上一篇在NSIS中实现安装时取消并回滚(1)——现状提到的几个帖子有示例的。代码如下:(仅适用于MUI2)

!define MUI_PAGE_CUSTOMFUNCTION_PRE OnInstFilesPagePre ;必须放在下面的语句前面
!insertmacro MUI_PAGE_INSTFILES
Function OnInstFilesPagePre
GetDlgItem $0 $HWNDPARENT 2
EnableWindow $0 1
FunctionEnd

自定义取消函数的代码如下:

!define MUI_CUSTOMFUNCTION_ABORT OnUserAbort
Function OnUserAbort
…
nsExec::ExecToLog 'cmd /c copy /Y "$INSTDIR\install.log" "$INSTDIR\temp.log"'
Call CreateLogFromFile
Call RemoveDirectoriesFromLog
…
FunctionEnd

函数中给出的代码就是实现回滚操作部分的代码。在调用回滚函数(其实就是卸载时调用的根据日志删除文件的函数)前要把install.log复制一份,然后用复制的日志文件进行回滚。为什么要这样呢?因为后台的释放文件的线程被Terminate掉了,而就是那个线程打开的日志文件并写入安装日志,线程被和谐的,那么释放文件句柄的代码就不会被执行。说白了就是资源泄露了。后果就是再次打开这个文件的时候会失败,就算是只读也不行。用NSIS脚本举个例子。

FileOpen $0 “txt.txt” “w”
FileOpen $1 “txt.txt” “r” ;失败

第一次打开写,可以正确打开,第二次打开,虽然只读也会失败。但是如果像下面这样两次都是读。两次都是可以成功的。

FileOpen $0 “txt.txt” “r”
FileOpen $1 “txt.txt” “r” ;成功

上面只列出了功能实现的比较关键的代码。完整的代码可以从这里下载。如果要正确编译成安装包,还要下载这个头文件。

到此为止,基本在NSIS中实现了取消安装并回滚。但是这种实现有下面一些入缺陷。(都快成定律了,解决一个问题,引出三个问题。)

1. 回滚时install.log文件不能被删除。因为在上面已经解释了。

2. 回滚时,滚动条不回滚。其实应该是可以实现的。大家自己试下吧,应该不难。

3. 回滚后程序直接退出,而不会显示安装失败页面之类的页面。因为我们的回滚操作就是在User Abort里做的嘛。使用User Abort的后果就是直接退出了。应该也是有方法绕过去的

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容