Delphi codice per mostrare un messaggio amichevole se non hai il permesso di fare qualcosa che richiede privilegi di amministrazione, come installare un servizio
-
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;
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?