Как надежно прочитать отображаемое (имя и фамилию) имя пользователя во всех версиях Windows? - PullRequest
3 голосов
/ 10 сентября 2010

Я обнаружил, что в Windows 7 64 бит, на машине с доменным именем, GetUserNameEx (3, ....), который должен получить расширенный формат имени DisplayName (== 3), в буфер, работает нормально.

Однако он не работает в 32-разрядной версии Windows 7, то есть в рабочей группе, а не в домене, он возвращает ERROR_NONE_MAPPED.

Как прочитать дружеское имя человека«Фред Смит», например, таким образом, что работает на Windows?GetUserNameEx явно не работает.На самом деле, не сломанный, как мне сказали, просто не предназначен для работы для пользователей, которые не находятся в домене.Интересно, почему бы и нет, поскольку локальная информация SAM существует?И, похоже, нет другого прямого API для этого.

Если Windows выдает ERROR_NONE_MAPPED, вам не повезло, и, вероятно, нет в домене.Так что это не совсем дружественная область API.

[Похоже, можно вызвать NetUserGetInfo, чтобы прочитать локальную информацию SAM, когда нет домена, но вам нужно знатьсначала имя пользователя и пароль, а затем, возможно, будет найдено понятное имя.]

СВЯЗАННЫЙ Вопрос: здесь не упоминается проблема

Ответы [ 2 ]

7 голосов
/ 19 октября 2011

Вот решение Уоррена, портированное на 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;
}
3 голосов
/ 10 сентября 2010

У меня есть решение, которое, кажется, работает, что обычно означает:

  1. если работает функция GetUserNameEx (3, ...) из secur32.dll, используйте это значение.
  2. откат к комбинации вызовов GetUserNameEx (2, ...) и вызовов NetUserGetInfo, импортированных из netapi32.dll
  3. Проблема с первым вызовом NetUserGetInfo заключается в том, что он завершается сбоем для доменных имен или, по крайней мере, реализация ниже для NetUserGetInfo работает только при чтении информации SAM из имени локального компьютера в пространстве имен пользователя, не принадлежащем домену / не ActiveDirectory .

Пример кода (в Delphi), портированный на 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; Удалена неработающая ссылка.

...