How do you read the user's display (first and last) name on all versions of Windows reliably?

前端 未结 2 760
抹茶落季
抹茶落季 2020-12-19 17:13

I have found that on Windows 7 64 bit, on a machine with a domain name, GetUserNameEx( 3, .... ) which should get the extended name format DisplayName (==3), into a buffer,

2条回答
  •  春和景丽
    2020-12-19 18:04

    I have a solution that appears to work, which in general means:

    1. if the GetUserNameEx(3,...) function from secur32.dll works, use that value.
    2. fall back to a combination of GetUserNameEx(2,...) calls and NetUserGetInfo calls imported from netapi32.dll
    3. The problem with invoking NetUserGetInfo first, is that it fails on domain names, or at least, the implementation below on NetUserGetInfo works only when reading SAM information from a local machine name, on a non-domain/non-ActiveDirectory user namespace.

    Sample code (in Delphi), ported to C# below in Matt's answer:

    type
    EProcError = class( Exception );
    TGetUserNameExWProc = function( FormatType : Integer; Buffer : PWideChar; var BufSize : Integer ) : DWORD;      stdcall;
    var
      _GetUserNameExW : TGetUserNameExWProc;
    procedure GetProcedureAddress( var P : Pointer; const ModuleName, ProcName : string );
    var
      ModuleHandle : HMODULE;
    begin
      if not Assigned( P ) then
      begin
        ModuleHandle := GetModuleHandle( pChar( ModuleName ) );
        if ModuleHandle = 0 then
        begin
          ModuleHandle := SafeLoadLibrary( pChar( ModuleName ) );
          if ModuleHandle = 0 then
            raise EProcError.Create( 'Unable to load module' );
        end;
        P := GetProcAddress( ModuleHandle, pChar( ProcName ) );
        if not Assigned( P ) then
          raise EProcError.Create( 'Unable to get proc address' );
      end;
    end;
    function MyGetUserNameEx( aFormat : Integer ) : string;
    var
      sz : Integer;
      sz2 : Integer;
      ret : Integer;
    begin
      if not Assigned( _GetUserNameExW ) then
        GetProcedureAddress( Pointer( @_GetUserNameExW ), 'secur32.dll', 'GetUserNameExW' );
      if Assigned( _GetUserNameExW ) then
      begin
        sz := 2000;
        SetLength( Result, sz );
        Result[ 1 ] := Chr( 0 );
        ret := _GetUserNameExW( { 3=NameDisplay } aFormat, PWideChar( Result ), sz );
        if ret <> 0 then
        begin
          sz2 := StrLen( PWideChar( Result ) ); // workaround WinXP API bug
          if sz2 < sz then // WinXP bug.
            sz := sz2;
          SetLength( Result, sz )
        end
        else
        begin
          ret := GetLastError;
          if ret = ERROR_NONE_MAPPED then
            Result := ''
          else
            Result := 'E' + IntToStr( ret );
        end;
      end;
    end;
    function MyNetUserGetInfo : string;
    const
      netapi32 = 'netapi32.dll';
    type
      TNetUserGetInfo = function( servername, username : LPCWSTR; level : DWORD; var bufptr : PByte ) : DWORD; stdcall;
      TNetApiBufferFree = function( Buffer : PByte ) : DWORD; stdcall;
      USER_INFO_10 = record
        usri10_name : PWideChar;
        usri10_comment : PWideChar;
        usri10_usr_comment : PWideChar;
        usri10_full_name : PWideChar;
      end;
      P_USER_INFO_10 = ^USER_INFO_10;
    var
      _NetUserGetInfo : TNetUserGetInfo;
      _NetApiBufferFree : TNetApiBufferFree;
      ret : DWORD;
      servername : string;
      username : string;
      level : Cardinal;
      info : P_USER_INFO_10;
      pbuf : PByte;
      pwuser : PWideChar;
      n : Integer;
    begin
      ret := 0;
      _NetUserGetInfo := nil;
      GetProcedureAddress( Pointer( @_NetUserGetInfo ), netapi32, 'NetUserGetInfo' ); // raises EProcError
      if not Assigned( _NetUserGetInfo ) then
        Result := 'FunctionNotFound'
      else
      begin
        // usernamesize := 200;
        username := MyGetUserNameEx( 2 );
        if username = '' then
        begin
          Result := 'CanNotGetUserName';
          Exit;
        end;
        n := Pos( '\', username );      //' recover SO code formatting
        if n > 0 then
        begin
          servername := '\\' + Copy( username, 1, n - 1 );
          username := Copy( username, n + 1, Length( username ) );
        end;
        level := 10;
        pbuf := nil;
        pwuser := PWideChar( username );
        info := nil;
        if servername = '' then
          ret := _NetUserGetInfo( { servername } nil, pwuser, level, pbuf )
        else
          ret := _NetUserGetInfo( PWideChar( servername ), pwuser, level, pbuf );
        if ret = 0 then
        begin
          info := P_USER_INFO_10( pbuf );
          if Assigned( info ) then
            Result := info.usri10_full_name;
          GetProcedureAddress( Pointer( @_NetApiBufferFree ), netapi32, 'NetApiBufferFree' );
          if Assigned( info ) and Assigned( _NetApiBufferFree ) then
            _NetApiBufferFree( pbuf );
        end
        else
        begin
          if ret = 2221 then
            Result := 'Error_USER ' + username
          else if ret = 1722 then
            Result := 'Error_RPC ' + servername
          else
            Result := 'E' + IntToStr( ret );
        end;
      end;
    end;
    

    Edit: Nov 2011; Removed dead link.

提交回复
热议问题