UAC 并不是为了支持这一点而设计的,任何这样做的尝试都应该被视为 hack!
部分问题是您的进程中的 ShellExecuteEx 是提升操作的一部分,因此如果不终止线程或进程就无法终止它。为了解决这个问题,我允许设置充当我们可以杀死的中间人。这种设计的一个缺陷是 UAC 对话框在超时终止进程后仍然存在(UAC GUI 在以系统身份运行的许可.exe 中)这意味着用户可以在主安装程序消失后提升子安装程序!我只在 Win7 上对此进行了测试,如果父级消失了,UAC 不会启动新进程,但这可能是一个错误,或者至少该行为没有记录在 AFAIK 中,并且可能不应该在生产代码中使用!
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
在外部安装程序中使用,并且仅在您是管理员()时才运行子程序UserInfo::GetAccountType
。