Подтвердить что ты не робот

Сделать настройку привилегий запроса установщика Inno Setup только в случае необходимости

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

Это имеет некоторые недостатки:

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

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

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


Обратите внимание, что Inno Setup 6 имеет встроенную поддержку неадминистративного режима установки.

4b9b3361

Ответ 1

Inno Setup 6 имеет встроенную поддержку неадминистративного режима установки.

По сути, вы можете просто установить PrivilegesRequiredOverridesAllowed:

[Setup]
PrivilegesRequiredOverridesAllowed=commandline dialog

enter image description here


Ниже приведено мое (ныне устаревшее) решение для Inno Setup 5, основанное на @TLama answer.

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

  • Только в 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 >= $06000000);
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 '[email protected] stdcall';
function ShellExecute(hwnd: HWND; lpOperation: string; lpFile: string;
  lpParameters: string; lpDirectory: string; nShowCmd: Integer): THandle;
  external '[email protected] 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;

Ответ 2

В Inno Setup нет встроенного способа условного повышения процесса настройки во время его жизни. Тем не менее, вы можете выполнить процесс установки, используя глагол runas и убить неэквивалентный. script, который я написал, немного сложнее, но показывает, как это сделать.

Внимание:

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

[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 '[email protected] 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;