如何可靠地在所有版本的Windows上读取用户的显示(第一个和最后一个)名称?

时间:2010-09-10 15:59:42

标签: c# delphi winapi msdn

我发现在Windows 7 64位上,在具有域名的机器上,GetUserNameEx(3,....)应该获得扩展名称格式DisplayName(== 3),进入缓冲区,工作正常

但是,它不适用于Windows 7 32位,工作组上的虚拟机,而不是域,它会返回ERROR_NONE_MAPPED。

如何以一种适用于Windows的方式阅读该人的友好名称“Fred Smith”? GetUserNameEx明显被破坏了。实际上,没有破坏,我被告知,只是不打算为不在域上的用户工作。我想知道为什么不存在本地SAM信息?并且似乎没有其他直接的API可以做到这一点。

如果Windows为您提供ERROR_NONE_MAPPED,那么您运气不佳,可能不在域名上。所以这不是API的友好区域。

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

RElated Question: does not mention the problem here

2 个答案:

答案 0 :(得分:7)

这是Warren的解决方案移植到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;
}

答案 1 :(得分:3)

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

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

    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月;删除了死链接。