Сделать запрос Inno Setup installer повышение привилегий только при необходимости

Inno Setup установщик имеет PrivilegesRequired директива это может использоваться для управления, если требуется повышение привилегий, когда запускается установщик. Я хочу, чтобы мой установщик работал даже для пользователей, не являющихся администраторами (нет проблем с установкой моего приложения в папку пользователя, а не Program Files). Поэтому я установил PrivilegesRequired до none (недокументированное значение). Это делает всплывающее окно подсказки UAC только для пользователей администратора, поэтому они могут установить даже Program Files. Без запроса UAC для администратора пользователи, поэтому даже они могут установить приложение (в папку пользователя).

это имеет некоторые недостатки, хотя:

  • некоторые люди используют различные учетные записи admin и non-admin на своих машинах, обычно работая с учетной записью non-admin. В общем, при запуске установки с использованием учетной записи не администратора, когда они получают приглашение UAC, они вводят учетные данные для учетной записи администратора для продолжения. Но это не будет работать с моим установщиком, потому что нет UAC быстрый.
  • (чрезмерно подозрительные) люди с учетной записью администратора, которые хотят установить в папку пользователя, не могут запустить мой установщик без (НЕ необходимых) прав администратора.

есть ли способ сделать повышение привилегий Inno Setup request только при необходимости (когда пользователь выбирает папку установки, доступную только для записи учетной записью администратора)?

Я предполагаю, что для этого нет настроек в Inno Setup. Но, возможно, есть программное решение (Inno Setup Pascal скрипты) или какой-то плагин/DLL.

2 ответов


в Inno Setup нет встроенного способа для условного возвышения процесса установки в течение его жизни. Однако процесс установки можно выполнить с помощью runas глагол и убить не повышенного. Сценарий, который я написал, немного сложный, но показывает возможный способ, как это сделать.

предупреждение:

код, используемый здесь, всегда пытается выполнить экземпляр установки с повышенными правами; нет проверки, действительно ли требуется высота или нет (как решить, нужна ли высота дополнительно задать отдельный вопрос, пожалуйста). Кроме того, я не могу сказать в это время, безопасно ли делать такое ручное возвышение. Я не уверен, что Inno Setup не полагается (или не будет) на значение


мое решение на основе @TLama это.

при запуске установки без повышенных, он будет запрашивать повышение, за некоторыми исключениями:

  • только в Windows Vista и новее (хотя он должен работать и в Windows XP)
  • при обновлении программа установки проверит, имеет ли текущий пользователь доступ на запись в предыдущее место установки. Если у пользователя есть доступ на запись, программа установки не будет запрашивать повышение. Поэтому, если пользователь имеет ранее установленное приложение в папку пользователя, повышение не будет запрашиваться при обновлении.

если пользователь отклоняет высоту при новой установке, установщик автоматически возвращается в папку "данные локального приложения". Т. е. C:\Users\standard\AppData\Local\AppName.

другие изменения:

  • повышенный экземпляр не будет запрашивать язык снова
  • С помощью PrivilegesRequired=none, установщик напишет информацию об удалении в HKLM, когда повышенный, не HKCU.
#define AppId "myapp"
#define AppName "MyApp"

#define InnoSetupReg \
  "Software\Microsoft\Windows\CurrentVersion\Uninstall\" + AppId + "_is1"
#define InnoSetupAppPathReg "Inno Setup: App Path"

[Setup]
AppId={#AppId}
PrivilegesRequired=none
...

[Code]

function IsWinVista: Boolean;
begin
  Result := (GetWindowsVersion >= 000000);
end;

function HaveWriteAccessToApp: Boolean;
var
  FileName: string;
begin
  FileName := AddBackslash(WizardDirValue) + 'writetest.tmp';
  Result := SaveStringToFile(FileName, 'test', False);
  if Result then
  begin
    Log(Format(
      'Have write access to the last installation path [%s]', [WizardDirValue]));
    DeleteFile(FileName);
  end
    else
  begin
    Log(Format('Does not have write access to the last installation path [%s]', [
      WizardDirValue]));
  end;
end;

procedure ExitProcess(uExitCode: UINT);
  external 'ExitProcess@kernel32.dll stdcall';
function ShellExecute(hwnd: HWND; lpOperation: string; lpFile: string;
  lpParameters: string; lpDirectory: string; nShowCmd: Integer): THandle;
  external 'ShellExecuteW@shell32.dll stdcall';

function Elevate: Boolean;
var
  I: Integer;
  RetVal: Integer;
  Params: string;
  S: string;
begin
  { Collect current instance parameters }
  for I := 1 to ParamCount do
  begin
    S := ParamStr(I);
    { Unique log file name for the elevated instance }
    if CompareText(Copy(S, 1, 5), '/LOG=') = 0 then
    begin
      S := S + '-elevated';
    end;
    { Do not pass our /SL5 switch }
    if CompareText(Copy(S, 1, 5), '/SL5=') <> 0 then
    begin
      Params := Params + AddQuotes(S) + ' ';
    end;
  end;

  { ... and add selected language }
  Params := Params + '/LANG=' + ActiveLanguage;

  Log(Format('Elevating setup with parameters [%s]', [Params]));
  RetVal := ShellExecute(0, 'runas', ExpandConstant('{srcexe}'), Params, '', SW_SHOW);
  Log(Format('Running elevated setup returned [%d]', [RetVal]));
  Result := (RetVal > 32);
  { if elevated executing of this setup succeeded, then... }
  if Result then
  begin
    Log('Elevation succeeded');
    { exit this non-elevated setup instance }
    ExitProcess(0);
  end
    else
  begin
    Log(Format('Elevation failed [%s]', [SysErrorMessage(RetVal)]));
  end;
end;

procedure InitializeWizard;
var
  S: string;
  Upgrade: Boolean;
begin
  Upgrade :=
    RegQueryStringValue(HKLM, '{#InnoSetupReg}', '{#InnoSetupAppPathReg}', S) or
    RegQueryStringValue(HKCU, '{#InnoSetupReg}', '{#InnoSetupAppPathReg}', S);

  { elevate }

  if not IsWinVista then
  begin
    Log(Format('This version of Windows [%x] does not support elevation', [
      GetWindowsVersion]));
  end
    else
  if IsAdminLoggedOn then
  begin
    Log('Running elevated');
  end
    else
  begin
    Log('Running non-elevated');
    if Upgrade then
    begin
      if not HaveWriteAccessToApp then
      begin
        Elevate;
      end;
    end
      else
    begin
      if not Elevate then
      begin
        WizardForm.DirEdit.Text := ExpandConstant('{localappdata}\{#AppName}');
        Log(Format('Falling back to local application user folder [%s]', [
          WizardForm.DirEdit.Text]));
      end;
    end;
  end;
end;