| Platform SDK: Active Directory, ADSI, and Directory Services |
The following code fragment contains a function that uses ranging to retrieve the members of a group using IDirectoryObject:
////////////////////////////////////////////////////////////////////////////////////////////////////
/* PrintAttributeWithRanging() - Uses IDirectoryObject to read an attribute with ADSI ranging
Parameters
IDirectoryObject * pDirObject - Object from which the attribute will be read
LPOLESTR pwszAttribute - Name of the attribute
int iNumToDisplayAtATime - Number of Attributes to retrieve at a time
*/
HRESULT PrintAttributeWithRanging(IDirectoryObject * pDirObject,LPOLESTR pwszAttribute, int iNumToDisplayAtATime)
{
HRESULT hr = S_OK;
ADS_ATTR_INFO *pAttrInfo=NULL;
// Returned Data
DWORD dwReturn;
// number of returned elements
WCHAR pwszRangeAttrib[512];
// String for building Range
LPWSTR pAttrNames[]={pwszRangeAttrib};
// Array for passing attribute array
DWORD dwNumAttr =sizeof(pAttrNames)/sizeof(LPWSTR);
// Number of attributes requested
// Do a series of range searches, grabbing
// iNumToDisplayAtATime at a time.
int iRange = 0;
DWORD dAttribsGotten = 0;
while (SUCCEEDED(hr))
{
int iAttribsGottenLastSrch =0;
// Build the Range String : ex: L"Member;Range=0-5"
swprintf(pwszRangeAttrib,L"%s;Range=%d-%d",pwszAttribute,iRange,iRange+iNumToDisplayAtATime-1);
// Get the object Attributes
hr = pDirObject->GetObjectAttributes(pAttrNames,
dwNumAttr,
&pAttrInfo,
&dwReturn );
if(SUCCEEDED(hr))
{
// Loop through all the rows returned
for (DWORD x = 0; x< dwReturn; x++)
{
// Switch on TYPE returned
switch ((pAttrInfo+x)->dwADsType)
{
case ADSTYPE_DN_STRING:
case ADSTYPE_CASE_EXACT_STRING:
case ADSTYPE_CASE_IGNORE_STRING:
case ADSTYPE_PRINTABLE_STRING:
{
// If the returned value is multi-valued
//(which it is when using ranging),
// then loop through all the values in
//the multi-value
for (DWORD dwVal = 0 ; dwVal < (pAttrInfo+x)->dwNumValues; dwVal++)
{
// Print the values in the multi-valued
//return data
wprintf(L"%d: %s = %s\n",dAttribsGotten,(pAttrInfo+x)->pszAttrName,((pAttrInfo+x)->pADsValues+dwVal)->CaseIgnoreString);
// Increment some counters for checking
// whether LESS data was
// returned than was asked for (which
// means you are done)
dAttribsGotten++;
iAttribsGottenLastSrch++;
}
break;
}
default:
wprintf(L"%d: %s = !!UnHandled Type!!\n",dAttribsGotten,(pAttrInfo+x)->pszAttrName);
FreeADsMem( pAttrInfo );
pAttrInfo = NULL;
}
}
}
if (FAILED(hr) || hr ==S_ADS_NOMORE_ROWS || iAttribsGottenLastSrch== 0 ||
iAttribsGottenLastSrch< iNumToDisplayAtATime )
break;
// Increment the range counter, so the next string that is
// built will get the next range
iRange += iNumToDisplayAtATime;
}
// Use FreeADsMem for all memory obtained from ADSI call
return S_OK;
}
The following code fragment contains a subroutine that uses ranging to retrieve the members of a group:
'////////////////////////////////////////////////////////////////////////////////////////////////////
' PrintAttributeWithRanging() - Uses ADO to read an attribute with ADSI Ranging
'
' Parameters
'
' oDirObject pDirObject As IDirectoryObject - Object from which the attribute will be read
' ByVal sAttribute As String - Name of the attribute
' ByVal iNumToDisplayAtATime As Integer - Number of Attributes to retrieve at a time
'
'
Sub PrintAttributeWithRanging(oDirObject As IDirectoryObject, ByVal sAttribute As String, ByVal iNumToDisplayAtATime As Integer)
Dim iIndex As Integer
iIndex = 0
Dim j, i
Dim con As New Connection, rs As New Recordset
Dim Com As New Command
Dim oIADs As IADs
Dim sAdsPathRoot As String
' Get the LDAP path to the passed in object
sAdsPathRoot = GetAdsPath(oDirObject)
'Open a Connection object
con.Provider = "ADsDSOObject"
'-----------------------------------------------------------------
' To be authenticated using alternate credentials
' use connection properties of User ID and Password
'-----------------------------------------------------------------
' con.Properties("User ID") = "Administrator"
' con.Properties("Password") = ""
' Open the connection
con.Open "Active Directory Provider"
' Create a command object on this connection
Set Com.ActiveConnection = con
Dim iNumLastReturned As Integer
Dim iTotalAttribs As Integer
Dim iRange As Integer
iTotalAttribs = 0
iRange = 0
Do
' set the query string using SQL Dialect
Com.CommandText = "SELECT '" & sAttribute & ";Range=" & Trim(Str(iRange)) & "-" & Trim(Str(iRange + iNumToDisplayAtATime)) & "' FROM '" & sAdsPathRoot & "' WHERE CN='*'"
' Tell the user what the search filter is
DisplayMessage "Search Filter = " & Com.CommandText
'---------------------------------------------------
' Or you can use LDAP Dialect, for example,
'---------------------------------------------------
' Ex Com.CommandText = "<LDAP://Microsoft1/dc=Microsoft,DC=com>;(objectClass=*);name"
' For LDAP Dialect, the valid search scope are base, oneLevel and subtree
' Com.CommandText = "<" & adDomainPath & ">;(objectClass=*);name;subtree"
' For LDAP Dialect (<LDAP:...>), there is no way to specify sort order in the string,
' However, you can use this SORT ON property to specify sort order.
' for SQL Dialect you can use ORDER BY in the SQL Statement
' Ex. Com.Properties("Sort On") = "Name"
'Set the preferences for Search
Com.Properties("Page Size") = 1000
Com.Properties("Timeout") = 30 'seconds
Com.Properties("searchscope") = ADS_SCOPE_BASE
Com.Properties("Chase referrals") = ADS_CHASE_REFERRALS_EXTERNAL
Com.Properties("Cache Results") = False ' do not cache the result, it results in less memory requirements
'Execute the query
Set rs = Com.Execute
' Tell the user how many rows
DisplayMessage "Returned " & Str(rs.RecordCount) & " rows"
iNumLastReturned = 0
' Navigate the record set
If Not rs.EOF Then
rs.MoveFirst
End If
Dim f As Field
Dim iCount As Integer
iCount = 0
On Error Resume Next
While Not rs.EOF
For Each f In rs.Fields
' Is the value a Variant Array?
If TypeName(f.Value) = "Variant()" Then
Dim v As Variant
For Each v In f.Value
DisplayMessage f.Name & " (" & iTotalAttribs & ")" & "-" & v
iCount = iCount + 1
iNumLastReturned = iNumLastReturned + 1
iTotalAttribs = iTotalAttribs + 1
Next v
Else
' Otherwise display a single valued attribute
DisplayMessage f.Name & f.Value 'rs.Fields("AdsPath")
End If
Next f
rs.MoveNext
Wend
Set rs = Nothing
Set f = Nothing
iRange = iRange + iNumToDisplayAtATime
Loop While iNumLastReturned > 0
End Sub