Finally I spent almost a day to find out the way based on Win7 SDK documents. I have to use ADSI interface with LDAP path name to complete the complicated operation...
#define UNICODE
#include <stdio.h>
#include <wchar.h>
#include <iads.h>
#include <adshlp.h>
#pragma comment(lib,"Ole32.lib")
#pragma comment(lib,"Oleaut32.lib")
#pragma comment(lib,"Activeds.lib")
const IID iads={0xfd8256d0,0xfd15,0x11ce,{0xab,0xc4,0x02,0x60,0x8c,0x9e,0x75,0x53}};
const IID iads_group={0x27636b00,0x410f,0x11cf,{0xb1,0xff,0x02,0x60,0x8c,0x9e,0x75,0x53}}; // 27636b00-410f-11cf-b1ff-02608c9e7553
const IID iads_members={0x451a0030,0x72ec,0x11cf,{0xb0,0x3b,0x00,0xaa,0x00,0x6e,0x09,0x75}}; // 451a0030-72ec-11cf-b03b-00aa006e0975
const IID iid_enumvariant={0x00020404,0x0000,0x0000,{0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; // 00020404-0000-0000-C000-000000000046
void enum_members(IADsMembers *p_members)
{
IUnknown *p_unknown;
HRESULT hr0,hr1,hr2;
hr0=p_members->lpVtbl->get__NewEnum(p_members,&p_unknown);
if(hr0!=S_OK){
wprintf(L"2.0 Err=%08X\n",hr0);
}
else{
IEnumVARIANT *p_enum;
hr1=p_unknown->lpVtbl->QueryInterface(p_unknown,&iid_enumvariant,(void **)&p_enum);
if(hr1!=S_OK){
wprintf(L"2.1 Err=%08X\n",hr1);
}
else{
ULONG fetch;
VARIANT var;
VariantInit(&var);
hr2=p_enum->lpVtbl->Next(p_enum,1,&var,&fetch);
if(hr2!=S_OK){
wprintf(L"2.2 Err=%08X\n",hr2);
}
else{
int i=1;
while(hr2==S_OK){
IDispatch *p_disp;
if(fetch==1){
IADs *p_ads;
p_disp=V_DISPATCH(&var);
if(p_disp){
p_disp->lpVtbl->QueryInterface(p_disp,&iads,(void **)&p_ads);
if(p_ads){
BSTR str;
wchar_t member_name[100],member_class[100];
p_ads->lpVtbl->get_Name(p_ads,&str);
if(wcslen(str)<=100) wcscpy(member_name,str+3); // Don't copy "CN="
SysFreeString(str);
p_ads->lpVtbl->get_Class(p_ads,&str);
if(wcslen(str)<=100) wcscpy(member_class,str);
SysFreeString(str);
wprintf(L"Member %d: %ls (%ls)\n",i++,member_name,member_class);
SysFreeString(str);
p_ads->lpVtbl->Release(p_ads);
}
p_disp->lpVtbl->Release(p_disp);
}
}
VariantClear(&var);
p_disp=NULL;
hr2=p_enum->lpVtbl->Next(p_enum,1,&var,&fetch);
}
}
p_enum->lpVtbl->Release(p_enum);
}
p_unknown->lpVtbl->Release(p_unknown);
}
}
int wmain(int argc,wchar_t *argv[])
{
// Initialize COM.
CoInitialize(NULL);
wchar_t *group_ldap_name=L"LDAP://CN=xxx,OU=xxx,DC=xxx,DC=xxx";
HRESULT hr0,hr1;
IADsGroup *p_group;
hr0=ADsGetObject(group_ldap_name,&iads_group,(void **)&p_group);
if(hr0!=S_OK){
wprintf(L"0. Err=%08X\n",hr0);
}
else{
IADsMembers *p_members;
hr1=p_group->lpVtbl->Members(p_group,&p_members);
if(hr1!=S_OK){
wprintf(L"1. Err=%08X\n",hr1);
}
else{
enum_members(p_members);
p_members->lpVtbl->Release(p_members);
}
p_group->lpVtbl->Release(p_group);
}
// Uninitialize COM.
CoUninitialize();
return 0;
}