Inno Setup 需要管理员登录才能在重启后完成
Posted
技术标签:
【中文标题】Inno Setup 需要管理员登录才能在重启后完成【英文标题】:Inno Setup requires Administrator login to complete after restart 【发布时间】:2016-10-22 13:57:02 【问题描述】:似乎在我能找到的任何地方都没有这方面的信息,我宁愿不要等到它再次发生,然后再开始拼凑。不幸的是,因此,这是一个比我通常想发布的问题更广泛的问题,但由于我还没有确定原因、重现它、收集完整的细节并完整记录它,我只能等到它下一次发生能够收集和提供进一步的信息。所以,根据我所知道的,我希望有人能够对此有所了解。
似乎在某些情况下,Inno Setup 需要重新启动才能完成,这需要管理员在重新启动后登录。我认为这与文件注册有关,可能在System32
,由于其他更改已挂起,因此在安装时无法完成。
在发生这种情况的情况下,(我认为)Windows
目录中有三个随机命名的(类似于随机生成的temp
常量)文件,我(回想起来,经过一番思考)假设必须绑定到RunOnce
注册表项(我会在下次看到这种情况时查找),我(再次)假设在以管理员身份登录后运行以完成安装。它们似乎在管理员登录之前不会运行,如果标准用户登录,则安装处于不完整状态。一旦管理员登录,这些文件就会消失并且安装的应用程序按预期工作,而不是给出:
类未注册
在管理员登录并允许安装完全完成之前运行应用程序时出现的错误。
我想要做的是找到一种方法来确保安装完全完成,无论重新启动后登录的用户的权限如何,就好像应用程序是集中部署的(例如通过 SCCM),不会有管理员可用于在 PC 上登录,并且应用程序将不会运行,直到一个人这样做(这违背了使用 SCCM 之类的东西的意义)。实际上,我很惊讶 Inno Setup 没有自动处理这个问题,通过将文件设置为在下次登录时以 SYSTEM
帐户或类似方法运行。
如果有人可以大致解释这里发生了什么,如何找出文件的名称(我可以通过阅读 RunOnce
注册表项来做到这一点,但我需要知道我需要的值的名称阅读)以及运行它们和完成安装需要做什么,我应该能够通过例如使用计划任务在登录时以SYSTEM
运行,或其他一些方法来解决这个问题。
【问题讨论】:
【参考方案1】:你的假设是正确的。
当 Inno Setup 需要注册一些文件,但它也计算出需要重启机器才能完成一些安装,它会延迟注册直到重启之后。即使需要注册的实际文件已成功安装。
Inno Setup 将创建一个注册表项,如:
[HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\RunOnce]
"InnoSetupRegFile.0000000001"="\"C:\\WINDOWS\\is-NGP70.exe\" /REG /REGSVRMODE"
还有像这样的文件:
C:\Windows\is-NGP70.exe
C:\Windows\is-NGP70.lst
C:\Windows\is-NGP70.msg
.lst
文件包含要注册的文件列表:
[s.]C:\Program Files (x86)\My Program\MyClass.dll
注意is-???
名称是随机的,与安装程序的临时文件夹不同。
在日志文件中,您将看到:
2016-10-22 18:13:06.439 Delaying registration of all files until the next logon since a restart is needed.
2016-10-22 18:13:06.441 Registration executable created: C:\Users\martin\AppData\Local\Temp\is-NGP70.exe
确实,当安装程序以管理员权限运行时,is-???.exe
将在非管理员登录时静默不做任何事情。
但是如果您以非管理员权限运行安装程序,文件将被写入%TEMP%
; HKCU\...\RunOnce
的密钥;并且使用/REGU
开关代替/REG
开关;并且任何用户都将继续注册。
[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce]
"InnoSetupRegFile.0000000001"="\"C:\\Users\\user\\AppData\\Local\\Temp\\is-S5KU2.exe\" /REGU /REGSVRMODE"
为了轻松测试场景,只需创建一个简单的安装程序,注册任何.dll
并设置AlwaysRestart=yes
:
[Setup]
AlwaysRestart=yes
[Files]
Source: "MyClass.dll"; DestDir: "app"; Flags: regserver
如果是因为其他文件而不是需要注册的文件需要重启,可以考虑使用RegisterServer
function在Code
中注册.dll
,避免注册延迟。
RegisterServer(Is64Bit, ExpandConstant('app\MyClass.dll'), False);
使用上述语句的示例,请参见Inno Setup: How to remove Abort from regserver error?
或者,正如您已经建议自己的那样,您可以使用 Window Scheduler 安排具有管理员权限的“onlogon”任务来运行 is-???.exe
文件。
见How to make the program run on startup with admin permission with Inno Setup?
虽然您可以使用 [Run]
部分条目来运行 schtasks
(如我对上述问题的回答所示),但该部分仅在创建 RunOnce
条目后才会处理。但是您还需要删除 RunOnce
条目。您不能在[Registry]
部分执行此操作,因为该部分已被处理。您需要在 Pascal 脚本中对此进行编码。那么在代码中两者都做可能会更好。
您可以运行schtasks
(使用Exec
function)并从CurStepChanged(ssPostInstall)
event function 中删除条目(使用RegDeleteValue
function)。
您还需要删除该任务。也许您可以使用/Z
开关来“在最终运行后标记要删除的任务”。但我不确定这可以与/SC onlogon
结合使用。如果没有,您需要在任务中运行schtasks /Delete
。
【讨论】:
我在回答中添加了更多细节。 我可以确认这是由于在PrepareToInstall
中作为安装的一部分运行 .NET Framework 先决条件安装后将 NeedRestart
函数设置为 True
造成的。至少在这种情况下,解决此问题的最佳方法是将NeedRestart
函数设置为True
移动到安装结束,而不是在开始时。这样做首先可以防止这种情况发生,并避免需要更复杂的解决方案。
但是NeedRestart
在文件注册之前只被调用一次。所以这没有任何意义。
安装程序将在以下情况下重新启动: 1) 它无法替换某些文件,因为它们正在使用中。 2) NeedRestart
返回真。 3) 你使用AlwaysRestart
指令;或带有某些组件或任务的restart
标志 4) 某些Run
程序安排重新启动后文件重命名。 - 所以你几乎可以控制一切,除了文件替换。
如果在您之前执行的其他基于 Inno Setup 的安装程序有待注册,则可以有更多 is-????.exe
。我认为您几乎可以放心地假设最高的InnoSetupRegFile.???
是您的。【参考方案2】:
感谢 Martin 的回答,这是我想出的代码(经过测试和工作)来解决这个问题,以防它帮助其他人。请注意,这有额外的好处(至少对我而言)运行其他产品使用 Inno Setup 进行安装所需的任何和所有挂起的文件注册(需要管理员登录),无论创建它们的安装程序是什么。
procedure CurStepChanged(CurStep: TSetupStep);
var
intRegFileNumber, intIndex, intCmdFileRegNumber: Integer;
strRegFileCmd, strRegFileNumber, strCmdFileRegNumber: String;
arrRegFileLines: TArrayOfString;
intResultCode: Integer;
begin
//Run additional tasks after the installation finishes i.e. after the [Run] section completes
if CurStep = ssPostInstall then
begin
//File registrations after restart require Administrator login fix
if RegValueExists(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce', 'InnoSetupRegFile.0000000001') then
begin
intRegFileNumber := 1;
strRegFileNumber := Format('%.10d', [intRegFileNumber]);
intIndex := 1;
intCmdFileRegNumber := 0;
strCmdFileRegNumber := Format('%.3d', [intCmdFileRegNumber]);
SetArrayLength(arrRegFileLines, 100);
arrRegFileLines[0] := '@echo off';
while RegValueExists(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce', 'InnoSetupRegFile.' + strRegFileNumber) do
begin
RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce', 'InnoSetupRegFile.' + strRegFileNumber, strRegFileCmd);
RegDeleteValue(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce', 'InnoSetupRegFile.' + strRegFileNumber);
arrRegFileLines[intIndex] := strRegFileCmd;
intRegFileNumber := intRegFileNumber + 1;
strRegFileNumber := Format('%.10d', [intRegFileNumber]);
intIndex := intIndex + 1;
end;
while FileExists(ExpandConstant('win\is-filereg' + strCmdFileRegNumber + '.cmd')) do
begin
intCmdFileRegNumber := intCmdFileRegNumber + 1;
strCmdFileRegNumber := Format('%.3d', [intCmdFileRegNumber]);
end;
arrRegFileLines[intIndex] := 'start cmd.exe /c "timeout.exe /t 2 /nobreak & schtasks.exe /delete /f /tn "Inno Setup File Registrations ' + strCmdFileRegNumber + '" && del /f /q "%windir%\is-filereg' + strCmdFileRegNumber + '.cmd""';
arrRegFileLines[intIndex + 1] := 'cls';
arrRegFileLines[intIndex + 2] := 'exit';
SetArrayLength(arrRegFileLines, intIndex + 3);
SaveStringsToFile(ExpandConstant('win\is-filereg' + strCmdFileRegNumber + '.cmd'), arrRegFileLines, False);
Exec(ExpandConstant('sys\schtasks.exe'), '/create /ru "SYSTEM" /sc onstart /rl highest /f /tn "Inno Setup File Registrations ' + strCmdFileRegNumber + '" /tr "''' + ExpandConstant('win\is-filereg' + strCmdFileRegNumber + '.cmd') + '''', '', SW_HIDE,
ewWaitUntilTerminated, intResultCode);
end;
end;
end;
【讨论】:
start cmd.exe
生成一个单独的进程,允许由计划任务启动的进程完成,以便它可以删除计划任务和批处理文件(本身)。有点习惯,我将cls
和exit
添加到每个批处理文件以确保它关闭。我会看看你建议的其他改变。谢谢。
更新了代码以删除冗余循环并将if
更改为while
,因为除了检查一个文件之外,这将无法正常工作。将来我会考虑使用TStringList
,因为我不太熟悉如何使用这个函数并且它可以正常工作(尽管硬编码的上限为 100,但这应该足够了)。以上是关于Inno Setup 需要管理员登录才能在重启后完成的主要内容,如果未能解决你的问题,请参考以下文章