Platform SDK: Active Directory, ADSI, and Directory Services

Example Code for Using Ranging to Retrieve Members of a Group

[C++]

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;
}
[Visual Basic]

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