我有一个NSIS安装程序,其中有'RequestExecutionLevel用户'。该安装程序包含一个内置的NSIS安装程序,它具有'RequestExecutionLevel admin'。此子安装程序仅在某些情况下被调用。这样做是为了某些管理操作可以包含在子安装程序中,并且只有在必要时才会提示用户进行UAC批准。
我的问题是,我如何调用子安装程序,以便:
a)用户将被提示UAC对话框(如果启用了UAC),并且
b)如果用户在给定的时间段内没有点击"是"或"否",子安装程序将被终止。
我一直在使用ShellExecWait (http://nsis.sourceforge.net/ShellExecWait),它工作得很好,但不提供指定超时值的选项(即它将永远等待)。
请注意,我已经尝试使用以下NSIS函数:
* ShellExecWait -不允许超时
* ExecWait -子安装程序失败,因为它继承了父安装程序的执行级别。
* ExecShell -不允许超时
* nsExec::Exec -子安装程序失败,因为它继承了父安装程序的执行级别。
我实在是走投无路了,希望你能帮助我。
UAC不支持此功能,任何这样的尝试都应被视为黑客攻击!
问题的一部分是进程中的ShellExecuteEx是提升操作的一部分,所以如果不杀死线程或进程,就不能杀死它。为了解决这个问题,我允许设置充当我们可以杀死的中间人。这种设计的一个缺陷是,UAC对话框在超时结束进程后仍然存在(UAC GUI以SYSTEM的身份运行在permission .exe中),这意味着用户可以在主安装程序消失后提升子安装程序!我只在Win7上测试了这个,如果父进程消失了,UAC不会启动一个新的进程,但这可能是一个bug,或者至少该行为没有记录在案,可能不应该在生产代码中使用!
Outfile "test.exe"
RequestExecutionlevel User
!include LogicLib.nsh
!include WinMessages.nsh
!include FileFunc.nsh
Page Instfiles
!macro ElevateWithTimeout_OnInit
${GetParameters} $0
${GetOptions} $0 '--ExecTimer' $1
${If} $1 != ""
StrCpy $1 $0 "" 12
ExecShell 'runas' $1 ;RunAs is not really required as long as the .exe has a manifest that requests elevation...
Quit
${EndIf}
!macroend
Function ElevateWithTimeout
InitPluginsDir
System::Call '*(&i60,tsr1)i.r0'
StrCpy $1 "--ExecTimer $1"
System::Call '*$0(i 60,i 0x40,i $HwndParent,i0,t"$ExePath",tr1,to,i1)i.r0'
System::Call 'shell32::ShellExecuteEx(ir0)i.r1'
System::Call '*$0(i,i,i,i,i,i,i,i,i,i,i,i,i,i,i.r2)'
System::Free $0
Pop $0 ; Timeout (MS)
${If} $1 <> 0
System::Call 'kernel32::WaitForSingleObject(ir2,ir0)i.r0'
${If} 0x102 = $0 ;WAIT_TIMEOUT
System::Call 'kernel32::TerminateProcess(ir2,i666)'
${EndIf}
System::Call 'kernel32::CloseHandle(ir2)'
${EndIf}
FunctionEnd
Function .onInit
!insertmacro ElevateWithTimeout_OnInit
FunctionEnd
Section
Push 30000
Push "regedit.exe"
call ElevateWithTimeout
SectionEnd
为了创建一个更安全更健壮的解决方案,子安装程序必须在游戏中,并且知道当父程序离开时何时中止自己,但在纯NSIS代码中这样做是太多的工作。
我可能会建议您放弃超时要求并在外部安装程序中使用RequestExecutionlevel highest
,并且只有在您是admin (UserInfo::GetAccountType
)时才运行子进程。