Enumerate Windows Usernames - Delphi

DarkCoderSc
Jean-Pierre LESUEUR
uses
System.SysUtils, Winapi.Windows, Generics.Collections;
// ...
const MAX_PREFERRED_LENGTH = $FFFFFFFF;
NERR_Success = 0;
FILTER_NORMAL_ACCOUNT = 2;
USER_INFO_2_LVL = 2;
// ...
type
NetApiStatus = DWORD;
USER_INFO_2 = record
usri2_name : LPWSTR;
usri2_password : LPWSTR;
usri2_password_age : DWORD;
usri2_priv : DWORD;
usri2_home_dir : LPWSTR;
usri2_comment : LPWSTR;
usri2_flags : DWORD;
usri2_script_path : LPWSTR;
usri2_auth_flags : DWORD;
usri2_full_name : LPWSTR;
usri2_usr_comment : LPWSTR;
usri2_parms : LPWSTR;
usri2_workstations : LPWSTR;
usri2_last_logon : DWORD;
usri2_last_logoff : DWORD;
usri2_acct_expires : DWORD;
usri2_max_storage : DWORD;
usri2_units_per_week : DWORD;
usri2_logon_hours : Pointer;
usri2_bad_pw_count : DWORD;
usri2_num_logons : DWORD;
usri2_logon_server : LPWSTR;
usri2_country_code : DWORD;
usri2_code_page : DWORD;
end;
TUserInfo2 = USER_INFO_2;
PUserInfo2 = ^TUserInfo2;
// ...
// https://learn.microsoft.com/fr-fr/windows/win32/api/lmaccess/nf-lmaccess-netuserenum?WT_mc_id=SEC-MVP-5005282
function NetUserEnum(
servername : LPWSTR;
level : DWORD;
filter : DWORD;
var bufptr : Pointer;
prefmaxlen : DWORD;
var entriesread : DWORD;
var totalentries : DWORD;
resume_handle : Pointer
) : NetApiStatus; stdcall; external 'Netapi32.dll';
// https://learn.microsoft.com/fr-fr/windows/win32/api/lmaccess/nf-lmaccess-netusergetinfo?WT_mc_id=SEC-MVP-5005282
function NtUserGetInfo(
servername : LPWSTR;
username : LPWSTR;
level : DWORD;
var bufptr : Pointer
) : NetApiStatus; stdcall; external 'Netapi32.dll';
// https://learn.microsoft.com/en-us/windows/win32/api/lmapibuf/nf-lmapibuf-netapibufferfree?WT_mc_id=SEC-MVP-5005282
function NetApiBufferFree(
Buffer : Pointer
) : NetApiStatus; stdcall; external 'Netapi32.dll';
// ...
function EnumerateWindowsUsers(var AUsernames : TList<String>) : Cardinal;
begin
if not Assigned(AUsernames) then
AUsernames := TList<String>.Create()
else
AUsernames.Clear();
///
var AStatus : NetApiStatus;
repeat
var AResume : DWORD := 0;
var pBuffer : Pointer;
var AEntriesRead : DWORD;
var ATotalEntries : DWORD;
AStatus := NetUserEnum(
nil,
USER_INFO_2_LVL,
FILTER_NORMAL_ACCOUNT,
pBuffer,
MAX_PREFERRED_LENGTH,
AEntriesRead,
ATotalEntries,
@AResume
);
case AStatus of
NERR_SUCCESS, ERROR_MORE_DATA : begin
var pUserInfo : PUserInfo2 := pBuffer;
for var I := 0 to AEntriesRead -1 do begin
AUsernames.Add(WideCharToString(pUserInfo^.usri2_name));
///
Inc(pUserInfo);
end;
///
NetApiBufferFree(pBuffer);
end;
// See Microsoft Documentation to Handle Possible Errors:
// https://learn.microsoft.com/fr-fr/windows/win32/api/lmaccess/nf-lmaccess-netuserenum#return-value
// (ERROR_ACCESS_DENIED, ERROR_INVALID_LEVEL, NERR_BufTooSmall, NERR_InvalidComputer)
end;
until AStatus <> ERROR_MORE_DATA;
///
result := AUsernames.Count;
end;
// ...
var AUsernames := TList<String>.Create();
try
EnumerateWindowsUsers(AUsernames);
for var AUsername in AUsernames do
WriteLn(AUsername);
// ...
finally
FreeAndNil(AUsernames);
end;
Creating and researching code snippets takes time and effort. You’re welcome to share them through your own platforms, but please don’t forget to credit the original author, here: Jean-Pierre LESUEUR.
Implemented By Technique
Featured Windows APIs
Created
April 23, 2025
Last Revised
April 23, 2025