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

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

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

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

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

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

相关问题:这里没有提到问题

这是Warren的解决方案移植到C#。 我添加了从域名中检索域控制器的IP,因为至少在我的域上,只使用\\因为服务器名称不起作用。

 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 \\ 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. 如果secur32.dll的GetUserNameEx(3,…)函数有效,请使用该值。
  2. 回退到从netapi32.dll导入的GetUserNameEx(2,…)调用和NetUserGetInfo调用的组合
  3. 首先调用NetUserGetInfo的问题是它在域名上失败,或者至少,NetUserGetInfo上的实现仅在从非域/非ActiveDirectory用户命名空间的本地机器名读取SAM信息时起作用。

示例代码(在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月; 删除了死链接。