Inno Setup l'installateur a la directive PrivilegesRequired
qui peut être utilisée pour contrôler, si l'élévation des privilèges est requise, lorsque l'installateur démarre. Je souhaite que mon programme d'installation fonctionne même pour les utilisateurs non administrateurs (aucun problème concernant l'installation de mon application dans le dossier utilisateur, au lieu de Program Files
). J'ai donc défini le PrivilegesRequired
sur none
(valeur non documentée). Cela rend le popup UAC Prompt réservé aux utilisateurs administrateurs, afin qu'ils puissent même s'installer sur le Program Files
. Pas d'invite UAC pour les utilisateurs non administrateurs, donc même eux peuvent installer l'application (dans le dossier utilisateur).
Cela présente cependant certains inconvénients:
Existe-t-il un moyen de rendre l'élévation des privilèges de demande d'Inno Setup uniquement en cas de besoin (lorsque l'utilisateur sélectionne le dossier d'installation accessible en écriture par le compte administrateur uniquement)?
Je suppose qu'il n'y a aucun paramètre pour cela dans Inno Setup. Mais il existe peut-être une solution de programmation (script Inno Setup Pascal) ou une sorte de plugin/DLL.
Notez que Inno Setup 6 a un support intégré pour mode d'installation non administratif .
Inno Setup 6 a un support intégré pour mode d'installation non administratif .
Fondamentalement, vous pouvez simplement définir PrivilegesRequiredOverridesAllowed
:
[Setup]
PrivilegesRequiredOverridesAllowed=commandline dialog
Ce qui suit est ma solution (désormais obsolète) pour Inno Setup 5, basée sur @ TLama's answer .
Lorsque la configuration est lancée sans élévation, elle demandera une élévation, à quelques exceptions près:
Si l'utilisateur rejette l'élévation lors d'une nouvelle installation, le programme d'installation reviendra automatiquement au dossier "données d'application locale". C'est à dire. C:\Users\standard\AppData\Local\AppName
.
Autres améliorations:
PrivilegesRequired=none
, le programme d'installation écrit les informations de désinstallation dans HKLM
, lorsqu'il est élevé, et non dans 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;
Il n'y a aucun moyen intégré d'élévation conditionnelle du processus de configuration pendant sa durée de vie dans Inno Setup. Cependant, vous pouvez exécuter le processus d'installation en utilisant le verbe runas
et tuer le verbe non élevé. Le script que j'ai écrit est un peu délicat, mais montre une façon possible de le faire.
Avertissement:
Le code utilisé ici tente d'exécuter toujours l'instance d'installation élevée; il n'y a pas de vérification si l'élévation est réellement requise ou non (comment décider si l'élévation est nécessaire, éventuellement demander dans une question séparée, s'il vous plaît). De plus, je ne peux pas dire pour le moment s'il est sécuritaire de faire une telle élévation manuelle. Je ne sais pas si Inno Setup ne s'appuie pas (ou ne compte pas) sur la valeur de la directive PrivilegesRequired
d'une manière ou d'une autre. Et enfin, ce truc d'élévation ne devrait être exécuté que sur les versions Windows connexes. Aucune vérification n'est effectuée dans ce 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;