Delphi 代码,如果您无权执行需要管理员权限的操作(例如安装服务),则显示友好消息



客户报告我们的应用程序在安装期间出现错误,这表明当由域管理员执行安装时,下面的'IsWindowsAdministrator'中的代码返回FALSE。以本地管理员身份登录,一切正常。安装程序(Innosetup)调用一个exe文件,该文件执行一些服务控制管理器操作(见下文),但在调用下面的IsWindowsAdministrator来检查用户状态之后。

我想要检查管理状态的原因是在调用服务管理器任务来使用驱动程序之前提供一个优雅的错误(参见下面的驱动程序安装代码)。这些都是我无法在Innosetup中轻松完成的任务,我选择将它们打包到安装程序调用的小exe中。

CHECK ADMIN 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

========= 检查管理代码 ================

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;

与其检查用户是否是管理员,不如直接检查来自OpenSCManager()和/或OpenService()的错误代码。如果用户权限不足,GetLastError()将返回ERROR_ACCESS_DENIED,例如:

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; 

我在一个大多数操作不需要管理员权限的应用程序中使用这种技术来检测何时在Vista+下手动调用UAC提示以获得足够的权限,并且它工作得很好。

因为您正在从安装程序中运行可执行文件,您可能需要做的是为请求提升的可执行文件创建一个清单。不要问你有什么特权。向windows请求所需的权限。

如何为windows安装程序创建清单?

最新更新