Example Code for Ranging with IDirectorySearch

The following code example uses ranging to retrieve the members of a group using the IDirectorySearch interface.

HRESULT EnumGroupWithIDirectorySearch(LPCWSTR pwszGroupDN, 
                                      LPCWSTR pwszUsername, 
                                      LPCWSTR pwszPassword)
{
    if(NULL == pwszGroupDN)
    {
        return E_POINTER;
    }
    
    HRESULT hr;
    IDirectorySearch *pSearch;

    hr = ADsOpenObject( pwszGroupDN,
                        pwszUsername,
                        pwszPassword,
                        ADS_SECURE_AUTHENTICATION,
                        IID_IDirectorySearch, 
                        (void**)&pSearch);

    if(SUCCEEDED(hr))
    {
        const DWORD dwStep = 1000;
        DWORD dwLowRange;
        DWORD dwHighRange;
        BOOL fLastQuery;
        BOOL fExit;

        // Only search for properties of this object.
        ADS_SEARCHPREF_INFO rgSearchPrefs[1];
        rgSearchPrefs[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
        rgSearchPrefs[0].vValue.dwType = ADSTYPE_INTEGER;
        rgSearchPrefs[0].vValue.Integer = ADS_SCOPE_BASE;

        // Set the search preference.
        hr = pSearch->SetSearchPreference(rgSearchPrefs, 1);
        if (FAILED(hr))
        {
            return hr;
        }

        dwLowRange = 0;
        dwHighRange = dwLowRange + (dwStep - 1);

        // Set the search filter.
        LPWSTR pszSearch = L"(CN=*)";

        fLastQuery = FALSE;
        fExit = FALSE;

        do
        {
            WCHAR wszName[] = L"name";
            WCHAR wszAttribute[MAX_PATH];
            ADS_SEARCH_HANDLE hSearch;

            if(fLastQuery)
            {
                // Perform this query with the "range=<lowRange>-*" range.
                swprintf_s(wszAttribute, L"member;range=%d-*", dwLowRange);
            }
            else
            {
                // Perform this query with the "range=<lowRange>-<highRange>" range.
                swprintf_s(wszAttribute, L"member;range=%d-%d", dwLowRange, dwHighRange);
            }
  
            OutputDebugStringW(L"Query:");
            OutputDebugStringW(wszAttribute);
            OutputDebugStringW(L"\n");

            LPWSTR rgAttributes[2] = {wszName, wszAttribute};

            // Execute the query.
            hr = pSearch->ExecuteSearch(pszSearch, rgAttributes, 2, &hSearch);
            if(SUCCEEDED(hr))
            {
                // Enumerate the rows.
                while(SUCCEEDED(hr = pSearch->GetNextRow(hSearch)))
                {
                    if(S_OK == hr)
                    {
                        LPWSTR pwszColumnName;
                        hr = pSearch->GetNextColumnName(hSearch, &pwszColumnName);
                        if(SUCCEEDED(hr))
                        {
                            /*
                            If the column name retrieved from the server is 
                            different than the query string, this indicates that 
                            last range requested was beyond the range of 
                            property values. Perform one last query with the 
                            "range=<lowRange>-*" range.
                            */
                            if(0 != lstrcmpiW(pwszColumnName, wszAttribute))
                            {
                                if(fLastQuery)
                                {
                                    /*
                                    The request failed to retrieve the attribute in
                                    two of two attempts. This will occur if the group has
                                    no members. Exit the loop to avoid an infinite loop
                                    condition.
                                    */
                                    fExit = TRUE;
                                }

                                fLastQuery = TRUE;

                                FreeADsMem(pwszColumnName);

                                break;
                            }
                            else
                            {
                                ADS_SEARCH_COLUMN col;
                                
                                // Get the column.
                                hr = pSearch->GetColumn(hSearch, pwszColumnName, &col);
                                if(SUCCEEDED(hr))
                                {
                                    DWORD i;

                                    // Enumerate the retrieved property values.
                                    for(i = 0; i < col.dwNumValues; i++)
                                    {
                                        wprintf(L"%s\n", col.pADsValues[i].CaseIgnoreString);
                                    }

                                    // Free the column.
                                    pSearch->FreeColumn(&col);
                                }

                                FreeADsMem(pwszColumnName);

                                if(fLastQuery)
                                {
                                    /*
                                    The last query was just completed, so exit the loop.
                                    */
                                    fExit = TRUE;
                                    break;
                                }
                            }
                        }
                    }
                    else if(S_ADS_NOMORE_ROWS == hr)
                    {
                        /*
                        Call ADsGetLastError to verify that the search is waiting 
                        for a response to a previous query.
                        */
                        DWORD dwError = ERROR_SUCCESS;
                        WCHAR szError[512];
                        WCHAR szProvider[512];
                            
                        ADsGetLastError(&dwError, szError, 512, szProvider, 512);
                        if(ERROR_MORE_DATA != dwError)
                        {
                            break;
                        }
                        /*
                        The search is waiting for a response to a previous 
                        query. Call GetNextRow again.
                        */
                    }
                    else
                    {
                        break;
                    }

                }

                // Close the search handle to cleanup.
                pSearch->CloseSearchHandle(hSearch);

                // If the last query was performed, exit the loop.
                if(!fLastQuery)
                {
                    // Increment the high and low ranges to query for the next block of objects.
                    dwLowRange = dwHighRange + 1;
                    dwHighRange = dwLowRange + (dwStep - 1);
                }
            }
        }while(!fExit);

        pSearch->Release();
    }

    return hr;
}