繁体   English   中英

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

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

我发现在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信息,当不在域上时,但您需要首先知道用户名和密码,然后它可能会查找友好名称。]

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

这是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. 如果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月; 删除了死链接。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM