我正在使用 NSIS 构建一个简单的安装程序,并希望在批处理脚本中以静默模式运行它,最后我会像这样检查错误级别:
REM a simple test
REM get into folder and run the installer
cd /d my_installer_path
my_installer_made_with_nsis.exe /S
if %ERRORLEVEL% NEQ 0 echo something has gone wrong
现在,似乎 my_installer_made_with_nsis.exe 总是返回
%ERRORLEVEL% 0
,即使在 nsi 代码中我明确设置了错误级别并以 Abort 退出。
在我的 nsi 脚本中,我使用了这样的 ErrorHandling 函数
Function ErrorHandler
IfSilent +2 0
MessageBox MB_ICONSTOP "$err_msg"
DetailPrint "$err_msg"
SetErrorlevel 5 ; <---- APPARENTLY DOES NOT WORK
Abort
FunctionEnd
然后,在我的代码中,我这样调用错误处理程序:
...
StrCpy $err_msg "An exception occurred: blah blah"
Call ErrorHandler
我期望在 cmd shell 外部 %ERRORLEVEL% 为 5,但它始终为 0。
也许我弄错了 NSIS 中 ErrorLevel 的整个概念,但在这种情况下,有没有办法从命令 shell 检索安装程序的退出代码?
SetErrorlevel
工作正常,这意味着它设置了进程的退出代码。 %ERRORLEVEL%
不一定是子进程的退出代码,特别是当您调用 GUI 进程时。你的问题是你的batch-file/cmd.exe,而不是NSIS。
Function MyAbortFunction
SetErrorlevel 5
Abort
FunctionEnd
Section
System::Call 'Kernel32::GetEnvironmentVariable(t "TestAbort", p 0, i0)i.r0'
${If} $0 = 0
System::Call 'Kernel32::SetEnvironmentVariable(t "TestAbort", t "1")'
ExecWait '"$ExePath"' $0 ; Run the test instance of our self
MessageBox MB_OK "Exit code = $0" ; This will be 5
SetErrorlevel $0 ; Make the parent use the same code in case you are testing in cmd.exe
${Else}
Call MyAbortFunction
${EndIf}
SectionEnd
在 cmd.exe 中可以正常工作:
start "" /WAIT "c:\my\path\to\Test.exe"
echo %errorlevel%
nsi 脚本没有任何问题
解决办法是: 在批处理脚本中 使用
start /wait
运行安装程序
就这么简单,但对于 NSIS 初学者来说并不是那么明显。
如果您必须等待安装程序在批处理脚本中继续进行,那么使用
start /wait
就成为必要,但一开始您可能会错过这一点,并且在谈到 ERRORLEVEL 时也会错过效果。
NSIS 手册有许多运行静默卸载程序的好示例 http://nsis.sourceforge.net/Docs/Chapter4.html#silent
但它没有提到
start /wait
的需要
如果以手动为例:
foo.exe /S /D=C:\Program Files\Foo
(并且假设 foo 非常简单并且为了测试而FAST)
当您在 cmd shell 中运行该命令时,您一开始无法意识到 shell 没有等待。然后,如果您检查错误级别,您会感到惊讶(像我一样)
如果您想在交互模式下运行 foo.exe 时测试错误级别,可能会发生同样的情况。您无法意识到 foo 与 shell “分离”。在这种情况下检查 shell %ERRORLEVEL% 是没有用的。
最后你发现有人建议必须使用启动/等待非控制台应用程序
http://forums.winamp.com/showpost.php?p=2530597&postcount=2
所以你应该使用这个命令行:
start /wait foo.exe /S /D=C:\Program Files\Foo`
在PowerShell中,你可以大致这样实现:
$process = Start-Process -FilePath .\foo.exe -ArgumentList "/S" -PassThru
$process.WaitForExit()
$errorlevel = $process.ExitCode