仅在需要时使Inno Setup安装程序请求权限提升

Mar*_*ryl 24 privileges uac inno-setup pascalscript elevated-privileges

如果需要特权提升,Inno Setup安装程序具有可用于控制安装程序启动时的PrivilegesRequired指令.我希望我的安装程序甚至可以为非管理员用户工作(没有问题将我的应用程序安装到用户文件夹,而不是Program Files).所以我设置PrivilegesRequirednone(未记录的值).这使得UAC仅为管理员用户提示弹出,因此他们甚至可以安装到Program Files.没有UAC提示非管理员用户,所以即使他们可以安装应用程序(到用户文件夹).

这有一些缺点:

  • 有些人在他们的计算机上使用不同的管理员和非管理员帐户,通常使用非管理员帐户.通常,在使用非管理员帐户启动安装时,当他们收到UAC提示时,他们会输入管理员帐户的凭据以继续.但这不适用于我的安装程序,因为没有UAC提示.
  • (过于可疑)拥有管理员帐户且想要安装到用户文件夹的人无法在没有(不需要)管理员权限的情况下启动我的安装程序.

是否有一些方法可以在需要时(当用户选择仅由管理员帐户写入的安装文件夹时)使Inno Setup请求权限提升?

我假设在Inno Setup中没有这个设置.但可能有一个程序化的解决方案(Inno Setup Pascal脚本)或某种插件/ DLL.


请注意,即将推出的Inno Setup 6具有对非管理安装模式的内置支持.

Mar*_*ryl 15

请注意,即将推出的Inno Setup 6具有对非管理安装模式的内置支持.


我的解决方案基于@ TLama的答案.

当设置以非提升方式启动时,它将请求提升,但有一些例外:

  • 仅适用于Windows Vista及更高版本(尽管它也适用于Windows XP)
  • 升级时,安装程​​序将检查当前用户是否具有对先前安装位置的写入权限.如果用户具有写访问权限,则安装程序将不会请求提升.因此,如果用户先前已将应用程序安装到用户文件夹,则升级时将不会请求提升.

如果用户拒绝新安装的提升,安装程序将自动回退到"本地应用程序数据"文件夹.即PrivilegesRequiredOverridesAllowed.

其他改进:

  • 提升的实例不会再要求语言
  • 通过使用C:\Users\standard\AppData\Local\AppName,安装程序会在卸载PrivilegesRequired=none时将卸载信息写入,而不是HKLM.
[Setup]
PrivilegesRequiredOverridesAllowed=commandline dialog
Run Code Online (Sandbox Code Playgroud)


TLa*_*ama 9

在Inno Setup中,没有内置的方法可以在设置过程中有条件地提升设置过程.但是,您可以使用runas动词执行设置过程并终止非提升的过程.我写的脚本有点棘手,但显示了一种可行的方法.

警告:

这里使用的代码总是尝试执行提升的设置实例; 没有检查是否实际需要提升(如何确定是否需要提升,可选择在单独的问题中提出请求).另外,我现在还不知道,这样手动升降是否安全.我不确定Inno Setup是否(或不会)PrivilegesRequired以某种方式依赖指令的值.最后,此高程内容应仅在相关的Windows版本上执行.在此脚本中没有检查这个:

[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
PrivilegesRequired=lowest

[Code]
#ifdef UNICODE
  #define AW "W"
#else
  #define AW "A"
#endif
type
  HINSTANCE = THandle;

procedure ExitProcess(uExitCode: UINT);
  external 'ExitProcess@kernel32.dll stdcall';
function ShellExecute(hwnd: HWND; lpOperation: string; lpFile: string;
  lpParameters: string; lpDirectory: string; nShowCmd: Integer): HINSTANCE;
  external 'ShellExecute{#AW}@shell32.dll stdcall';

var
  Elevated: Boolean;
  PagesSkipped: Boolean;

function CmdLineParamExists(const Value: string): Boolean;
var
  I: Integer;  
begin
  Result := False;
  for I := 1 to ParamCount do
    if CompareText(ParamStr(I), Value) = 0 then
    begin
      Result := True;
      Exit;
    end;
end;

procedure InitializeWizard;
begin
  { initialize our helper variables }
  Elevated := CmdLineParamExists('/ELEVATE');
  PagesSkipped := False;
end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin
  { if we've executed this instance as elevated, skip pages unless we're }
  { on the directory selection page }
  Result := not PagesSkipped and Elevated and (PageID <> wpSelectDir);
  { if we've reached the directory selection page, set our flag variable }
  if not Result then
    PagesSkipped := True;
end;

function NextButtonClick(CurPageID: Integer): Boolean;
var
  Params: string;
  RetVal: HINSTANCE;
begin
  Result := True;
  { if we are on the directory selection page and we are not running the }
  { instance we've manually elevated, then... }
  if not Elevated and (CurPageID = wpSelectDir) then
  begin
    { pass the already selected directory to the executing parameters and }
    { include our own custom /ELEVATE parameter which is used to tell the }
    { setup to skip all the pages and get to the directory selection page }
    Params := ExpandConstant('/DIR="{app}" /ELEVATE');
    { because executing of the setup loader is not possible with ShellExec }
    { function, we need to use a WinAPI workaround }
    RetVal := ShellExecute(WizardForm.Handle, 'runas',
      ExpandConstant('{srcexe}'), Params, '', SW_SHOW);
    { if elevated executing of this setup succeeded, then... }
    if RetVal > 32 then
    begin
      { exit this non-elevated setup instance }
      ExitProcess(0);
    end
    else
    { executing of this setup failed for some reason; one common reason may }
    { be simply closing the UAC dialog }
    begin
      { handling of this situation is upon you, this line forces the wizard }
      { stay on the current page }
      Result := False;
      { and possibly show some error message to the user }
      MsgBox(Format('Elevating of this setup failed. Code: %d', [RetVal]),
        mbError, MB_OK);
    end;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

  • @TLama这种方法的问题是InnoSetup会将卸载信息写入HKCU而不是HKLM ......显然它是由`PrivilegesRequired`控制的.有办法解决吗?请参见http://stackoverflow.com/q/42655340/98713 (2认同)