UAC はこれをサポートするように設計されておらず、これを行う試みはハッキングと見なされるべきです!
問題の一部は、プロセス内の ShellExecuteEx が昇格操作の一部であるため、スレッドまたはプロセスを強制終了せずに強制終了できないことです。これを回避するために、セットアップが私たちが殺すことができる仲介者として機能できるようにします。この設計の 1 つの欠点は、タイムアウトによってプロセスが強制終了された後も UAC ダイアログが残っていることです (UAC GUI は、システムとして実行されている consent.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
)。