我发现在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信息,当不在域上时,但您需要首先知道用户名和密码,然后它可能会查找友好的名。]
答案 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)
我有一个似乎有效的解决方案,通常意味着:
示例代码(在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月;删除了死链接。