当前位置: 首页IT技术 → 便携软件制作的系列教程

便携软件制作的系列教程

更多

在NSIS中怎么导入注册表。

这有何难,用registry插件嘛:

${registry::RestoreKey} file.reg $var

可是,如果你经常在 RestoreKey 后面用 ${registry:write} ,就会发现,往往导入注册表会失败,或者写入的键值被reg文件中的旧键值覆盖了,这是为什么呢?

原来,${registry::RestoreKey} 这个命令并不会等待导入完成。作者在文档中写了:

${registry::RestoreKey} simply exec regedit: regedit /s “[file]“

执行的是 Exec 而非 ExecWait 。那么,可能 regedit.exe 尚未启动,就开始执行下一行命令了。制作一般的安装包问题不大,但便携软件对执行步骤的顺序要求更加精确。所以,有些人的代码是这样写的:

${registry::RestoreKey} file.reg $0

Sleep 200

睡一会。睡多久?睡一秒还是一年,这种盲人摸象的做法,我们完美主义者是不会使用的。因为这个命令,有些朋友凡是用到registry插件,都习惯性地加上个 sleep,这是完全没有必要的,作者说了:

问:So my question is, what other functions in your plugin behave in the same way (ie do not wait for the registry operation to finish)?

答:registry::RestoreKey is the only one.

那么,用:

ExecWait 'regedit /s "[file]"' $var

不就行了吗?

你又错了,我们制作便携软件的时候,要对自己严格要求,在Vista以上的系统中,不经过UAC验证,是无法执行 regedit /s 这个命令的(即使导入HKCU中的键值也不行)。难道你的每个软件都要用户通过UAC验证以管理员权限运行吗,完全是别有居心!

可是,在UAC环境的测试中,你会发现,即使不通过UAC验证,${registry::RestoreKey} 这个命令也可以完成注册表导入,难道,作者隐瞒了什么?

于是,作为代码盲的你,充满狐疑地打开 NSIS\Include\Registry.nsh ,找到这样一段代码:

!define registry::RestoreKey !insertmacro registry::RestoreKey

!macro registry::RestoreKey _FILE _ERR

registry::_RestoreKey /NOUNLOAD ${_FILE}

Pop ${_ERR}

IntCmp ${_ERR} -2 0 0 +10 ;REGEDIT4 ansi file

SetDetailsPrint none

IfFileExists "$SYSDIR\reg.exe" 0 +4 ;reg.exe used in Windows2K/XP/Vista/7

nsExec::ExecToStack "$SYSDIR\reg.exe" import "${_FILE}"

Pop ${_ERR}

StrCmp ${_ERR} 0 +5 0

IfFileExists "$WINDIR\regedit.exe" 0 +3 ;regedit.exe used in Wine

ExecWait "$WINDIR\regedit.exe" /s "${_FILE}" ${_ERR}

IfErrors 0 +2

StrCpy ${_ERR} -1

SetDetailsPrint lastused

!macroend

真是狡兔三窟!registry::RestoreKey失败后,用reg.exe import,失败后,又用 regedit.exe /s,我们就要有这种不屈不挠的精神,不要让一次执行的失败变成Bug。

眼尖的你发现,关键在于这一行:

nsExec::ExecToStack "$SYSDIR\reg.exe" import "${_FILE}"

原来,虽然regedit /s需要管理员权限,但reg import命令并不需要,这就是${registry::RestoreKey}成功的秘诀。

但是,${registry::RestoreKey}首先尝试用插件导入,而插件并不等待导入结束,所以,我们在应用的时候,要把顺序颠倒一下:

nsExec::ExecToStack "$SYSDIR\reg.exe" import "${_FILE}"

Pop $0

${IfNot} $0 == 0

${registry::RestoreKey} "${_FILE}" $0

Sleep 500

${IfNotThen} $0 == 0 ${|} StrCpy ${_OutVar} Error ${|}

${Endif}

nsExec::ExecToStack是等待运行结束的,首先执行,假如失败,再用${registry::RestoreKey},并暂停0.5秒(比较安全的数值)。当以上动作始终返回Error的时候,我们就应该考虑做个标记,在便携软件结束的时候跳过这一次软件运行中的注册表修改,不覆盖原先的reg文件了。

不过,当你翻阅 PortableApps.com Launcher 的源代码时,却发现关于注册表导入,仅仅用了一行:

${registry::RestoreKey} $DataDirectory\settings\$0.reg $R9

可为什么感觉上PAL那么稳定,极少出错呢?我猜是因为PAL的代码非常繁杂,每个实际动作以前都有一堆工作,又是读Launcher.ini,又是转换变量,又是检测PAF平台,慢悠悠的,慢工出细活吧!

热门评论
最新评论
昵称:
表情: 高兴 可 汗 我不要 害羞 好 下下下 送花 屎 亲亲
字数: 0/500 (您的评论需要经过审核才能显示)