I am trying to call the Winapi function NetGroupGetUsers:
The NetGroupGetUsers function retrieves a list of the members in a particular global group in the security database, which is the security accounts manager (SAM) database or, in the case of domain controllers, the Active Directory.
I've tried calling the function with every variation of serverName
and groupName
i can think of:
ServerName | GroupName | Error code | Description |
---|---|---|---|
null | docker-users | 2220 | The group name could not be found |
null | OBSIDIAN\docker-users | 2220 | The group name could not be found |
null | .\docker-users | 2220 | The group name could not be found |
OBSIDIAN | docker-users | 2220 | The group name could not be found |
OBSIDIAN | OBSIDIAN\docker-users | 2220 | The group name could not be found |
OBSIDIAN | .\docker-users | 2220 | The group name could not be found |
. | docker-users | 2220 | The group name could not be found |
. | OBSIDIAN\docker-users | 2220 | The group name could not be found |
. | .\docker-users | 2220 | The group name could not be found |
Where the error code 2220
corresponds to the constant:
NERR_GroupNotFound
: The global group name in the structure pointed to by bufptr parameter could not be found.
Long Version
There is a group on my local workstation called docker-users
:
>whoami /groups
GROUP INFORMATION
-----------------
Group Name Type SID Attributes
===================== ===== ============================================= ==================================================
OBSIDIAN\docker-users Alias S-1-5-21-502352433-3072756349-3142140079-1006 Mandatory group, Enabled by default, Enabled group
And you can see the group members in netplwiz
:
You can also see the group members in the Local Users and Groups MMC snap-in.
- Starting with the SID (e.g.
S-1-5-21-502352433-3072756349-3142140079-1006
) - Then call LookupAccountSID to have it return:
- DomainName
- AccountName
- SID type
- If the SidType represents a group (i.e. SidTypeAlias, SidTypeWellKnownGroup, or SidTypeGroup - which all mean "group")
We want the group members. So we call NetGroupGetUsers:
NetGroupGetUsers(null, "
DomainName\
AccountName", 1, out buffer, MAX_PREFERRED_LENGTH, out entriesRead, out totalEntries, null);
Except it fails. Every time. No matter what.
The question
Why does the call fail?
What is the correct way to specify:
- Server name
- group name
Of course, for all i know it could be an ABI alignment issue. The only way to find out is if someone else tries calling the function on their local (domain joined or non-domain joined PC - doesn't matter).
CRME for the lazy
program GetGroupUsersDemo;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Windows;
function NetGroupGetUsers(servername: LPCWSTR; groupname: LPCWSTR; level: DWORD;
out bufptr: Pointer; prefmaxlen: DWORD; out entriesread: DWORD;
out totalentries: DWORD; ResumeHandle: PDWORD): DWORD; stdcall; external 'netapi32.dll';
function GetGroupMembers(ServerName, GroupName: UnicodeString): HRESULT;
var
res: DWORD; {NET_API_STATUS}
buf: Pointer;
entriesRead: DWORD;
totalEntries: DWORD;
i: Integer;
server: PWideChar;
const
MAX_PREFERRED_LENGTH = Cardinal(-1);
begin
server := PWideChar(ServerName);
if server = '' then
server := nil;
res := NetGroupGetUsers(server, PWideChar(GroupName), 1,
{var}buf,
MAX_PREFERRED_LENGTH, //Let the function allocate everything for us
{var}entriesRead,
{var}totalEntries,
nil);
Result := HResultFromWin32(res);
end;
procedure Test(ServerName, GroupName: UnicodeString);
var
hr: HRESULT;
s: string;
begin
hr := GetGroupMembers(ServerName, GroupName);
s := ServerName;
if s = '' then
s := 'null'; //can't have people not reading the question
Writeln('| '+s+' | '+GroupName+' | '+IntToStr(hr and $0000FFFF)+' | '+SysErrorMessage(hr)+' |');
end;
procedure Main;
begin
Writeln('| ServerName | GroupName | Error code | Description |');
Writeln('|------------|-----------|------------|-------------|');
Test('', 'OBSIDIAN\docker-users');
Test('', '.\docker-users');
Test('OBSIDIAN', 'docker-users');
Test('OBSIDIAN', 'OBSIDIAN\docker-users');
Test('OBSIDIAN', '.\docker-users');
Test('.', 'docker-users');
Test('.', 'OBSIDIAN\docker-users');
Test('.', '.\docker-users');
end;
begin
try
Main;
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Bonus Reading
- How to get members of a Windows (security) group?
- Usage of NetGroupGetUsers
- https://www.experts-exchange.com/questions/21985159/Group-not-found-error-on-NetGroupGetUsers.html
- https://www.experts-exchange.com/questions/11273935/NetGroupGetUsers-API.html
- https://microsoft.public.vb.winapi.networks.narkive.com/ssfpNHJR/problem-with-local-groups
- https://forum.powerbasic.com/forum/user-to-user-discussions/powerbasic-for-windows/5791-newbeeeeee-help-please