代码之家  ›  专栏  ›  技术社区  ›  Warren P

如何在所有版本的Windows上可靠地读取用户的显示(名字和姓氏)?

  •  3
  • Warren P  · 技术社区  · 14 年前

    我发现在64位Windows7上,在一台域名为GetUserNameEx(3,…)的机器上它应该将扩展名格式DisplayName(==3)放入缓冲区,工作正常。

    例如,你是如何在Windows上读这个人的友好名字“Fred Smith”的?GetUserNameEx显然已损坏。事实上,我被告知,并没有被破坏,只是不打算为不在某个域上的用户工作。为什么不呢,我想知道,既然本地的SAM信息存在?而且似乎没有其他直接API可以做到这一点。

    如果Windows给你错误映射,你就走运了,可能不在域上。所以这并不是API的友好领域。

    [当不在域中时,可以调用NetUserGetInfo,读取本地SAM信息,但您需要先知道用户名和密码,然后它可能会查找友好名称。]

    RElated Question: does not mention the problem here

    2 回复  |  直到 8 年前
        1
  •  7
  •   David Heffernan    11 年前

    以下是沃伦的解决方案移植到C#。我添加了从域名中检索域控制器的IP,因为至少在我的域中,使用 \\<domain> 因为服务器名不起作用。

    using System;
    using System.Text;
    using System.Net;
    using System.Runtime.InteropServices;
    using System.DirectoryServices.ActiveDirectory;
    
    [DllImport("secur32.dll", CharSet = CharSet.Auto)]
    private static extern int GetUserNameEx (int nameFormat, StringBuilder userName, ref uint userNameSize);
    
    [DllImport("netapi32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
    private static extern int NetUserGetInfo ([MarshalAs(UnmanagedType.LPWStr)] string serverName,
                                              [MarshalAs(UnmanagedType.LPWStr)] string userName,
                                              int level, out IntPtr bufPtr);
    
    [DllImport("netapi32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
    private static extern long NetApiBufferFree (out IntPtr bufPtr);
    
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct USER_INFO_10
    {
        [MarshalAs(UnmanagedType.LPWStr)] public string usri10_name;
        [MarshalAs(UnmanagedType.LPWStr)] public string usri10_comment;
        [MarshalAs(UnmanagedType.LPWStr)] public string usri10_usr_comment;
        [MarshalAs(UnmanagedType.LPWStr)] public string usri10_full_name;
    }
    
    private string getUserDisplayName ()
    {
        var username = new StringBuilder(1024);
        uint userNameSize = (uint) username.Capacity;
    
        // try to get display name and convert from "Last, First" to "First Last" if necessary
        if (0 != GetUserNameEx(3, username, ref userNameSize))
            return Regex.Replace(username.ToString(), @"(\S+), (\S+)", "$2 $1");
    
        // get SAM compatible name <server/machine>\\<username>
        if (0 != GetUserNameEx(2, username, ref userNameSize))
        {
            IntPtr bufPtr;
            try
            {
                string domain = Regex.Replace(username.ToString(), @"(.+)\\.+", @"$1");
                DirectoryContext context = new DirectoryContext(DirectoryContextType.Domain, domain);
                DomainController dc = DomainController.FindOne(context);
    
                if (0 == NetUserGetInfo(dc.IPAddress,
                                        Regex.Replace(username.ToString(), @".+\\(.+)", "$1"),
                                        10, out bufPtr))
                {
                    var userInfo = (USER_INFO_10) Marshal.PtrToStructure(bufPtr, typeof (USER_INFO_10));
                    return Regex.Replace(userInfo.usri10_full_name, @"(\S+), (\S+)", "$2 $1");
                }
            }
            finally
            {
                NetApiBufferFree(out bufPtr);
            }
        }
    
        return String.Empty;
    }
    
        2
  •  3
  •   Warren P    12 年前

    我有一个似乎有效的解决方案,通常意味着:

    1. 如果secur32.dll中的GetUserNameEx(3,…)函数起作用,请使用该值。
    2. 返回到从netapi32.dll导入的GetUserNameEx(2,…)调用和NetUserGetInfo调用的组合
    3. 首先调用NetUserGetInfo的问题是,它在域名上失败,或者至少,下面在NetUserGetInfo上的实现仅在从非域/非ActiveDirectory用户命名空间上的本地计算机名读取SAM信息时有效。

    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;
    

    编辑:2011年11月;删除死链接。