Сделать запрос 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;