Delphi codice per mostrare un messaggio amichevole se non hai il permesso di fare qualcosa che richiede privilegi di amministrazione, come installare un servizio

StackOverflow https://stackoverflow.com/questions/8344751

  •  27-10-2019
  •  | 
  •  

Domanda

Un Cliente segnala un errore dalla nostra applicazione durante l'installazione che indica che il codice in 'IsWindowsAdministrator' sotto sta tornando FALSE quando l'installazione viene eseguita da un amministratore di dominio. Accesso come amministratore locale tutto funziona bene. Il programma di installazione (Innosetup) chiama un file exe che fa alcune operazioni di Gestione controllo servizi (vedi sotto), ma dopo che l'IsWindowsAdministrator di seguito è stato chiamato per verificare lo stato dell'utente.

Il mio motivo per voler controllare lo stato di amministrazione è quello di fornire un errore di grazia prima di chiamare le attività di Service Manager al lavoro con un driver (vedi installazione driver codice qui sotto). Si tratta di compiti che non posso fare facilmente all'interno Innosetup e ho scelto di avvolgerli in un piccolo exe chiamato dal programma di installazione.

Il codice in CHECK CODICE ADMIN valida per questo compito? O dovrei semplicemente abbandonarla e avvolgere la chiamata al gestore di controllo del servizio in un try-se non con un messaggio di errore più bello?

Grazie

===================== DRIVER installare codice ======================= =

procedure ArtIODriver_Install( AShowSummary : boolean );
var
  hServiceControlManager : THandle;
  hService               : SC_HANDLE;
  ServiceStatus          : TServiceStatus;
  ServiceArgVectors      : PAnsiString;
begin

  If not IsWindowsAdministrator then
    Raise EArtIODriver.Create(
      'Error IODR4 - You must be a windows administrator to perform this action' );

  If not FileExists( ArtIODriver_FilePath ) then
    Raise EArtIODriver.CreateFmt(
      'Error IODR7 - Unable to locate the driver file "%s"',
      [ArtIODriver_FilePath] );

  hService := 0;
  hServiceControlManager := 0;
  try

    hServiceControlManager := OpenSCManager(
      nil,
      nil,
      SC_MANAGER_ALL_ACCESS);
    If hServiceControlManager = 0 then
      Raise EArtIODriver.CreateFmt(
        'Error IOD1 - Unable to open service control manager - %s',
        [GetWinLastErrorStr] );

    // can we see the service?
    hService := OpenService(
      hServiceControlManager,
      JustDriverName,
      SERVICE_ALL_ACCESS);
etc
etc

========= codice di controllo ADMIN ================

function IsWindowsAdministrator: Boolean;
// Returns TRUE if the user has administrator priveleges
// Returns a boolean indicating whether or not user has admin
// privileges. Call only when running under NT. Win9.x will return false!
var
  hAccessToken       : tHandle;
  ptgGroups          : pTokenGroups;
  dwInfoBufferSize   : DWORD;
  psidAdministrators : PSID;
  int                : integer;            // counter
  blnResult          : boolean;            // return flag

const
  SECURITY_NT_AUTHORITY: SID_IDENTIFIER_AUTHORITY =
    (Value: (0,0,0,0,0,5)); // ntifs
  SECURITY_BUILTIN_DOMAIN_RID: DWORD = $00000020;
  DOMAIN_ALIAS_RID_ADMINS: DWORD = $00000220;
  DOMAIN_ALIAS_RID_USERS : DWORD = $00000221;
  DOMAIN_ALIAS_RID_GUESTS: DWORD = $00000222;
  DOMAIN_ALIAS_RID_POWER_: DWORD = $00000223;

begin
  Result := False;
  blnResult := OpenThreadToken( GetCurrentThread, TOKEN_QUERY,
                                True, hAccessToken );
  if ( not blnResult ) then
  begin
    if GetLastError = ERROR_NO_TOKEN then
    blnResult := OpenProcessToken( GetCurrentProcess,
                       TOKEN_QUERY, hAccessToken );
  end;

  ptgGroups := nil;

  if ( blnResult ) then
  try

    GetMem(ptgGroups, 1024);
    blnResult := GetTokenInformation( hAccessToken, TokenGroups,
                                      ptgGroups, 1024,
                                      dwInfoBufferSize );
    CloseHandle( hAccessToken );

    if ( blnResult ) then
    begin

      AllocateAndInitializeSid( SECURITY_NT_AUTHORITY, 2,
                                SECURITY_BUILTIN_DOMAIN_RID,
                                DOMAIN_ALIAS_RID_ADMINS,
                    0, 0, 0, 0, 0, 0,
                    psidAdministrators );
      {$IFOPT R+}
        {$DEFINE RMINUS}
        {$R-}
      {$ENDIF}
      for int := 0 to ptgGroups.GroupCount - 1 do

        if EqualSid( psidAdministrators,
                     ptgGroups.Groups[ int ].Sid ) then
        begin
          Result := True;
          Break;
        end;
      {$IFDEF IMINUS}
        {$R-}
        {$UNDEF IMINUS}
      {$ENDIF}

      FreeSid( psidAdministrators );
    end;

  finally
    If ptgGroups <> nil then
      FreeMem( ptgGroups );
  end;
end;
È stato utile?

Soluzione

Invece di verificare se l'utente è un amministratore, si dovrebbe semplicemente controllare il codice di errore dal OpenSCManager() e / o OpenService() invece. Se l'utente non dispone di diritti sufficienti, GetLastError() tornerà ERROR_ACCESS_DENIED, ad esempio:

hServiceControlManager := OpenSCManager( 
  nil, 
  nil, 
  SC_MANAGER_ALL_ACCESS); 
If hServiceControlManager = 0 then 
Begin
  If GetLastError() = ERROR_ACCESS_DENIED then
    Raise EArtIODriver.Create('Error IODR4 - You must be a windows administrator to perform this action' ) 
  else
    Raise EArtIODriver.CreateFmt('Error IOD1 - Unable to open service control manager - %s', [GetWinLastErrorStr] );
End; 

Io uso questa tecnica in un app che non richiede diritti di amministratore per la maggior parte delle sue operazioni per rilevare quando manualmente invocare un prompt UAC in Vista + per guadagnare diritti sufficienti, e funziona molto bene.

Altri suggerimenti

Perché si esegue un file eseguibile dal proprio installatore, quello che probabilmente dovete fare è creare un manifesto per il vostro eseguibile che richiede elevazione. Non chiedere quali privilegi si dispone. Chiedi finestre per i privilegi necessari.

Come faccio a creare un manifesto per una finestra installatore?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top