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
 Windows Users Enumeration
                                            Windows Users Enumeration