I need to check what kind of accounts are available on a local machine.
I already found how I can do this for the current logged user:
function IsWindowsAdmin: Boolean;
var
hAccessToken: THandle;
ptgGroups: PTokenGroups;
dwInfoBufferSize: DWORD;
psidAdministrators: PSID;
g: Integer;
bSuccess: BOOL;
begin
Result:= False;
bSuccess:= OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True, hAccessToken);
if not bSuccess then
begin
if GetLastError = ERROR_NO_TOKEN then
bSuccess:= OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, hAccessToken);
end;
if bSuccess then
begin
GetMem(ptgGroups, 1024);
bSuccess:= GetTokenInformation(hAccessToken, TokenGroups, ptgGroups, 1024, dwInfoBufferSize);
CloseHandle(hAccessToken);
if bSuccess then
begin
AllocateAndInitializeSid(SECURITY_NT_AUTHORITY, 2,
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0, psidAdministrators);
for g:= 0 to ptgGroups.GroupCount - 1 do
if EqualSid(psidAdministrators, ptgGroups.Groups[g].Sid) then
begin
Result:= True;
Break;
end;
FreeSid(psidAdministrators);
end;
FreeMem(ptgGroups);
end;
end;
I also found how to get all users on a local machine. Is it possible to check for any user if it is an administrator or a limited account without having to log in each and every user?
Here's my take on the subject, thanks to Sertac Akyuz for the hint about NetUserEnum
with the USER_INFO_2
request and structure.
We can request users with following privilege levels
const
USER_PRIV_GUEST = 0;
USER_PRIV_USER = 1;
USER_PRIV_ADMIN = 2;
USER_PRIV_ANY = 3; // own invention
type
TPrivLevel = USER_PRIV_GUEST..USER_PRIV_ANY;
Some declarations we need
const
// some consts
NERR_Success = 0;
MAX_PREFERRED_LENGTH = $FFFFFFFF;
type
NetApiStatus = DWORD;
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa371337(v=vs.85).aspx
TUserInfo2 = 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: PBYTE ;
usri2_bad_pw_count: DWORD ;
usri2_num_logons: DWORD ;
usri2_logon_server: LPWSTR ;
usri2_country_code: DWORD ;
usri2_code_page: DWORD ;
end;
PUSER_INFO_2 = ^TUserInfo2;
LPUSER_INFO_2 = ^TUserInfo2;
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa370304(v=vs.85).aspx
function NetApiBufferFree (Buffer: Pointer): NetApiStatus ;
stdcall; external 'netapi32.dll';
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa370652%28v=vs.85%29.aspx
function NetUserEnum(
servername: LPCWSTR;
level: DWORD;
filter: DWORD;
var bufptr: pointer;
prefmaxlen: DWORD;
var entriesread: DWORD;
var totalentries: DWORD;
resume_handle: LPDWORD
): NetApiStatus; stdcall; external 'netapi32.dll';
And the procedure where PrivLevel
is the privilege level for which you are asking the users and
users
is the TStringList
to be populated with the user names.
function GetUsers(PrivLevel: TPrivLevel; users: TStrings): integer;
var
i: integer;
NetApiStatus: DWORD;
bufptr: pointer;
recptr: PUSER_INFO_2;
EntriesRead,
TotalEntries,
HResume: DWORD;
begin
HResume := 0;
repeat
NetApiStatus := NetUserEnum(
nil, // local
2, // USER_INFO_2
0, // no special filter
bufptr,
MAX_PREFERRED_LENGTH,
EntriesRead,
TotalEntries,
@HResume
);
if (NetApiStatus = NERR_Success) or (NetApiStatus = ERROR_MORE_DATA) then
begin
recptr := bufptr;
for i := 0 to EntriesRead-1 do
begin
if (PrivLevel = USER_PRIV_ANY) or (recptr^.usri2_priv = PrivLevel) then
users.Add(recptr^.usri2_name);
inc(recptr);
end;
NetApiBufferFree(bufptr);
end;
until NetApiStatus <> ERROR_MORE_DATA;
Result := NetApiStatus;
end;
Example of usage
procedure TForm1.Button1Click(Sender: TObject);
var
res: integer;
begin
res := GetUsers(USER_PRIV_ADMIN, Memo.Lines);
if res <> 0 then Memo.Lines.Add('Error getting users! Error code '+IntToStr(res));
end;
Edit
I changed GetUsers()
into a function and return the success/error code from the call to NetUserEnum()
Likely return values according to documentation
NERR_Success = 0
ERROR_ACCESS_DENIED = 5; // $0005
ERROR_INVALID_LEVEL = 124; // $007C
ERROR_MORE_DATA = 234; // $00EA
NERR_BufTooSmall = 2123; // $084B
NERR_InvalidComputer = 2351; // $092F
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With