Platform SDK: Active Directory, ADSI, and Directory Services

Example Code for Using the Global Catalog to Find Users in a Forest

[Visual Basic]

The following code finds all users in the forest by querying the global catalog using ADO:

Dim Con As ADODB.Connection
Dim ocommand As ADODB.Command
Dim gc As IADs

On Error Resume Next
'Maximum number of items to list on a msgbox.
MAX_DISPLAY = 5
 
'ADO Connection object
Set Con = CreateObject("ADODB.Connection")
  If (Err.Number <> 0) Then
     BailOnFailure Err.Number, "on CreateObject"
  End If
Con.Provider = "ADsDSOObject"
  If (Err.Number <> 0) Then
     BailOnFailure Err.Number, "on Provider"
  End If
Con.Open "Active Directory Provider"
  If (Err.Number <> 0) Then
     BailOnFailure Err.Number, "on Open"
  End If
'ADO Command object
Set ocommand = CreateObject("ADODB.Command")
  If (Err.Number <> 0) Then
     BailOnFailure Err.Number, "on CreateObject"
  End If
ocommand.ActiveConnection = Con
  If (Err.Number <> 0) Then
     BailOnFailure Err.Number, "on Active Connection"
  End If
Set gc = GetObject("GC:")
For Each child In gc
    Set entpr = child
Next
  If (Err.Number <> 0) Then
     BailOnFailure Err.Number, "on GetObject for GC"
  End If
show_items entpr.ADsPath, "foo"
 
ocommand.CommandText = "<" & entpr.ADsPath & ">;(&(objectCategory=person)(objectClass=user));distinguishedName,name;subTree"
  If (Err.Number <> 0) Then
     BailOnFailure Err.Number, "on CommandText"
  End If
Set rs = ocommand.Execute
  If (Err.Number <> 0) Then
     BailOnFailure Err.Number, "on Execute"
  End If
strText = "Found " & rs.RecordCount & " Users in Forest:"
intNumDisplay = 0
intCount = 0
' Navigate the record set
rs.MoveFirst
While Not rs.EOF
    intCount = intCount + 1
    strText = strText & vbCrLf & intCount & ") "
    For i = 0 To rs.Fields.Count - 1
        If rs.Fields(i).Type = adVariant And Not (IsNull(rs.Fields(i).Value)) Then
          strText = strText & rs.Fields(i).Name & " = "
          For j = LBound(rs.Fields(i).Value) To UBound(rs.Fields(i).Value)
             strText = strText & rs.Fields(i).Value(j) & " "
          Next
        Else
          strText = strText & rs.Fields(i).Name & " = " & rs.Fields(i).Value & vbCrLf
        End If
    Next
    intNumDisplay = intNumDisplay + 1
    'Display in msgbox if there are MAX_DISPLAY items to display
    If intNumDisplay = MAX_DISPLAY Then
        Call show_items(strText, "Users in forest")
        strText = ""
        intNumDisplay = 0
    End If
    rs.MoveNext
Wend
show_items strText, "foo"
'''''''''''''''''''''''''''''''''''''''
'Display subroutines
'''''''''''''''''''''''''''''''''''''''
Sub show_items(strText, strName)
    MsgBox strText, vbInformation, "Search GC for users" & strName
End Sub
 
Sub BailOnFailure(ErrNum, ErrText)    strText = "Error 0x" & Hex(ErrNum) & " " & ErrText
    MsgBox strText, vbInformation, "ADSI Error"
    WScript.Quit
End Sub
[C++]

The following code fragment contains a function that finds all users in the forest by querying the global catalog:

HRESULT FindAllUsersInGC()
{
    HRESULT hr = E_FAIL;
    HRESULT hrGC = S_OK;
 
    VARIANT var;
    ULONG lFetch;
 
    // Interface Pointers
    IDirectorySearch *pGCSearch = NULL;
    IADsContainer *pContainer = NULL;
    IUnknown *pUnk = NULL;
    IEnumVARIANT *pEnum = NULL;
       IDispatch *pDisp = NULL;
    IADs *pADs = NULL;
 
 
 
    //Bind to global catalog
    hr = ADsOpenObject(L"GC:",
                NULL,
                NULL,
                ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
                IID_IADsContainer,
                (void**)&pContainer);
 
    if (SUCCEEDED(hr))
    {
       hr = pContainer->get__NewEnum( &pUnk );
        if (SUCCEEDED(hr))
        {
            hr = pUnk->QueryInterface( IID_IEnumVARIANT, (void**) &pEnum );
            if (SUCCEEDED(hr))
            {
            // Now Enumerate--there should be only one item.
            hr = pEnum->Next( 1, &var, &lFetch );
            if (SUCCEEDED(hr))
            {
                while( hr == S_OK )
                {
                    if ( lFetch == 1 )
                    {
                        pDisp = V_DISPATCH(&var);
                        hr = pDisp->QueryInterface( IID_IDirectorySearch, (void**)&pGCSearch); 
                        hrGC = hr;
                    }
                    VariantClear(&var);
                    hr = pEnum->Next( 1, &var, &lFetch );
                };
            }
        }
        if (pEnum)
            pEnum->Release();
        }
        if (pUnk)
        pUnk->Release();
    }
    if (pContainer)
        pContainer->Release();
 
 
    if (FAILED(hrGC))
    {
        if (pGCSearch)
            pGCSearch->Release();
        return hrGC;
    }
 
    //Create search filter
    LPOLESTR pszSearchFilter = L"(&(objectCategory=person)(objectClass=user))";
    //Search entire subtree from root.
    ADS_SEARCHPREF_INFO SearchPrefs;
    SearchPrefs.dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
    SearchPrefs.vValue.dwType = ADSTYPE_INTEGER;
    SearchPrefs.vValue.Integer = ADS_SCOPE_SUBTREE;
    DWORD dwNumPrefs = 1;
 
    // COL for iterations
    ADS_SEARCH_COLUMN col;
 
    // Handle used for searching
    ADS_SEARCH_HANDLE hSearch;
 
    // Set the search preference
    hr = pGCSearch->SetSearchPreference( &SearchPrefs, dwNumPrefs);
    if (FAILED(hr))
        return hr;
    // Set attributes to return
    CONST DWORD dwAttrNameSize = 2;
    LPOLESTR pszAttribute[dwAttrNameSize] = {L"cn",L"distinguishedName"};
 
    // Execute the search
    hr = pGCSearch->ExecuteSearch(pszSearchFilter,
                                  pszAttribute,
                                    dwAttrNameSize,
                                    &hSearch
                                    );
    if ( SUCCEEDED(hr) )
    { 
 
    // Call IDirectorySearch::GetNextRow() to retrieve the next row 
    //of data
        while( pGCSearch->GetNextRow( hSearch) != S_ADS_NOMORE_ROWS )
        {
 
            // loop through the array of passed column names,
            // print the data for each column
            for (DWORD x = 0; x < dwAttrNameSize; x++)
            {
 
                // Get the data for this column
                hr = pGCSearch->GetColumn( hSearch, pszAttribute[x], &col );
 
                if ( SUCCEEDED(hr) )
                {
                    // Print the data for the column and free the column
                    // Note the requested attributes are type CaseIgnoreString.
                    wprintf(L"%s: %s\r\n",pszAttribute[x],col.pADsValues->CaseIgnoreString); 
                    pGCSearch->FreeColumn( &col );
                }
                else
                    wprintf(L"<%s property is not a string>",pszAttribute[x]);
            }
            wprintf(L"------------------------------\n");
        }
 
        // Close the search handle to clean up
        pGCSearch->CloseSearchHandle(hSearch);
    } 
    if (pGCSearch)
        pGCSearch->Release();
    return hr;
}