Delphi code to show friendly message if you don't have permission to do something that requires admin priveleges such as install a service

A customer is reporting an error from our application during installation which indicates that the code in 'IsWindowsAdministrator' below is returning FALSE when the installation is performed by a domain administrator. Logged on as local admin it all works fine. The installer (Innosetup) calls an exe file which does some service control manager operations (see below) but after the IsWindowsAdministrator below has been called to check the user status.

My reason for wanting to check admin status is to provide a graceful error before calling service manager tasks to work with a driver (see DRIVER INSTALL CODE below). These are tasks that I cannot easily do inside Innosetup and I chose to wrap them into a small exe called by the installer.

Is the code in CHECK ADMIN CODE valid for this task? Or should I simply abandon it and wrap the call to the service control manager in a try-except with a nicer error message?

Thanks

===================== DRIVER INSTALL CODE ========================

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

========= CHECK ADMIN CODE ================

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;

Answers


Rather than checking if the user is an admin, you should simply check the error code from OpenSCManager() and/or OpenService() instead. If the user does not have sufficient rights, GetLastError() will return ERROR_ACCESS_DENIED, eg:

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; 

I use this technique in an app that does not require admin rights for most of its operations to detect when to manually invoke a UAC prompt under Vista+ to gain sufficient rights, and it works very well.


Need Your Help

CMake does not find OpenCV libraries

c++ opencv linker cmake make

Similarly to this question, I have troubles using OpenCV and CMake on OS X 10.10.3.

ASP.NET Web API Put request unable to find method in controller

asp.net-mvc json api web

I am trying to update the value of an album using a web api service, but when I use the put request it gets to the controller (sometimes), creates the context, but is then doesn't find/run the PUT ...

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.