tutorial setup programa personalizados para instaladores inno descargar crear avanzado inno-setup uac privileges pascalscript elevated-privileges

inno setup - setup - Haga que el instalador Inno solicite la elevación de privilegios solo cuando sea necesario



inno setup tutorial avanzado (2)

Inno Setup installer tiene la directiva PrivilegesRequired que se puede usar para controlar, si se requieren privilegios de elevación, cuando se inicia el instalador. Quiero que mi instalador funcione incluso para usuarios que no sean administradores (no hay problema con la instalación de mi aplicación en la carpeta de usuario, en lugar de los Program Files ). Así que puse los PrivilegesRequired en none (valor none documentado). Esto hace que UAC aparezca de forma emergente solo para usuarios de administración, para que puedan instalarse incluso en los Program Files . No hay mensaje UAC para usuarios que no son administradores, por lo que incluso ellos pueden instalar la aplicación (en la carpeta del usuario).

Esto tiene algunos inconvenientes, sin embargo:

  • Algunas personas usan distintas cuentas de administrador y de no administrador en sus máquinas, y normalmente trabajan con cuentas de no administrador. En general, cuando inician la instalación con una cuenta que no es de administración, cuando reciben una solicitud de UAC, ingresan las credenciales para que la cuenta de administrador continúe. Pero esto no funcionará con mi instalador, porque no hay un indicador de UAC.
  • Las personas (demasiado sospechosas) con una cuenta de administrador, que desean instalar en la carpeta de usuario, no pueden iniciar mi instalador sin los privilegios de administrador (no necesarios).

¿Hay alguna manera de hacer que los privilegios de solicitud de Inno Setup aumenten solo cuando sea necesario (cuando el usuario selecciona la carpeta de instalación con capacidad de escritura solo para la cuenta de administrador)?

Supongo que no hay configuración para esto en Inno Setup. Pero posiblemente, hay una solución programática (Inno Setup Pascal scripting) o algún tipo de plugin / DLL.


Mi solución basada en la respuesta de @ TLama .

Cuando la configuración se inicie de forma no elevada, se solicitará la elevación, con algunas excepciones:

  • Solo en Windows Vista y versiones posteriores (aunque también debería funcionar en Windows XP)
  • Al actualizar, la configuración verificará si el usuario actual tiene acceso de escritura a la ubicación de instalación anterior. Si el usuario tiene acceso de escritura, la configuración no solicitará la elevación. Por lo tanto, si el usuario ya instaló la aplicación en la carpeta de usuario, no se solicitará la elevación en la actualización.

Si el usuario rechaza la elevación en una nueva instalación, el instalador automáticamente retrocederá a la carpeta de "datos de la aplicación local". Es decir, C:/Users/standard/AppData/Local/AppName .

Otras mejoras:

  • La instancia elevada no volverá a pedir lenguaje.
  • Al usar PrivilegesRequired=none , el instalador escribirá la información de desinstalación en HKLM , cuando esté elevado, no en 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;


No hay una forma integrada para la elevación condicional del proceso de configuración durante su vida útil en Inno Setup. Sin embargo, puede ejecutar el proceso de configuración utilizando el verbo runas y eliminar el no elevado. El guión que escribí es un poco complicado, pero muestra una forma posible de hacerlo.

Advertencia:

El código utilizado aquí intenta ejecutar la instancia de configuración elevada siempre; no se comprueba si la elevación es realmente necesaria o no (cómo decidir si la elevación es necesaria, pregunte en una pregunta aparte, por favor). Además, no puedo decir en este momento, si es seguro hacer tal elevación manual. No estoy seguro de si Inno Setup no confía (o no lo hará) en el valor de la directiva PrivilegesRequired de alguna manera. Y, por último, estas cosas de elevación deben ejecutarse solo en las versiones relacionadas de Windows. No se realiza ninguna comprobación de esto en este 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;