Inno Setup installer has the PrivilegesRequired
directive that can be used to control, if privileges elevation is required, when installer is starting. I want my installer to work even for non-admin users (no problem about installing my app to user folder, instead of the Program Files
). So I set the PrivilegesRequired
to none
(undocumented value). This makes UAC prompt popup for admin users only, so they can install even to the Program Files
. No UAC prompt for non-admin users, so even them can install the application (to user folder).
This has some drawbacks though:
Is there some way to make Inno Setup request privileges elevation only when needed (when user selects installation folder writable by admin account only)?
I assume there's no setting for this in Inno Setup. But possibly, there's a programmatic solution (Inno Setup Pascal scripting) or some kind of plugin/DLL.
Note that Inno Setup 6 has a built-in support for non-administrative install mode.
Inno Setup 6 has a built-in support for non-administrative install mode.
Basically, you can simply set PrivilegesRequiredOverridesAllowed
:
[Setup]
PrivilegesRequiredOverridesAllowed=dialog
Additionally, you will likely want to use the auto*
variants of the constants. Notably the {autopf}
for the DefaultDirName
.
[Setup]
DefaultDirName={autopf}\My Program
The following is my (now obsolete) solution for Inno Setup 5, based on @TLama's answer.
When the setup is started non-elevated, it will request elevation, with some exceptions:
If the user rejects the elevation on a new install, the installer will automatically fall back to "local application data" folder. I.e. C:\Users\standard\AppData\Local\AppName
.
Other improvements:
PrivilegesRequired=none
, the installer will write uninstall information to HKLM
, when elevated, not to 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 '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;