Platform SDK: Active Directory, ADSI, and Directory Services

Example Code for List Non-Replicated, Global Catalog, and Constructed Properties

The following C/C++ program queries for four types of properties that are 1) non-replicated, 2) indexed, 3) global catalog, and 4) constructed:

// Displays attributes of different types.
//
#include "stdafx.h"
#include <wchar.h>
#include <activeds.h>
 
HRESULT FindAttributesByType(IDirectorySearch *pSchemaNC, //IDirectorySearch pointer to schema naming context.
        DWORD dwAttributeType, //Bit flags to search for in systemFlags
        LPOLESTR pszAttributerNameType, //ldapDisplayName of the naming attribute you want to display for each returned attribute.
                                         //NULL returns common name.
        BOOL bIsExactMatch //TRUE to find attributes that have systemFlags exactly matching dwAttributeType
                            //FALSE to find attributes that have the dwAttributeType bit set (and possibly others).
        );
 
HRESULT FindIndexedAttributes(IDirectorySearch *pSchemaNC, //IDirectorySearch pointer to schema naming context.
        LPOLESTR pszAttributeNameType, //ldapDisplayName of the naming attribute you want to display for each returned attribute.
                                         //NULL returns common name.
        BOOL bIsIndexed //TRUE to find indexed attributes.
                        //FALSE to find non-indexed attributes.
        );
 
HRESULT FindGCAttributes(IDirectorySearch *pSchemaNC, //IDirectorySearch pointer to schema naming context.
        LPOLESTR pszAttributeNameType, //ldapDisplayName of the naming attribute you want to display for each returned attribute.
                                         //NULL returns common name.
        BOOL bInGC //TRUE to find indexed attributes.
                         //FALSE to find non-indexed attributes.
        );
 
int main(int argc, char* argv[])
{
HRESULT hr = E_FAIL;
LPOLESTR szPath = new OLECHAR[MAX_PATH];
IDirectorySearch *pSchemaNC = NULL;
IADs *pObject = NULL;
VARIANT var;
 
//Intialize COM
CoInitialize(NULL);
 
//Get rootDSE and the schema container's distinguished name.
//Bind to current user's domain using current user's security context.
hr = ADsOpenObject(L"LDAP://rootDSE",
                NULL,
                NULL,
                ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
                IID_IADs,
                (void**)&pObject);
if (SUCCEEDED(hr))
{
    hr = pObject->Get(L"schemaNamingContext",&var);
    if (SUCCEEDED(hr))
    {
    wcscpy(szPath,L"LDAP://");
    wcscat(szPath,var.bstrVal);
    hr = ADsOpenObject(szPath,
                    NULL,
                    NULL,
                    ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
                    IID_IDirectorySearch,
                    (void**)&pSchemaNC);
    if (SUCCEEDED(hr))
    {
    //Find non-replicated attributes
    wprintf(L"Find non-replicated attributes\n");
    hr = FindAttributesByType(pSchemaNC, //IDirectorySearch pointer to schema naming context.
        ADS_SYSTEMFLAG_ATTR_NOT_REPLICATED, //Bit flags to search for in systemFlags
        NULL,
        TRUE
        );
 
    //Find attributes included in the global catalog
    wprintf(L"Find attributes included in the global catalog\n");
    hr = FindGCAttributes(pSchemaNC,
        NULL,
        TRUE
        );
 
    //Find constructed attributes
    wprintf(L"Find constructed attributes\n");
    hr = FindAttributesByType(pSchemaNC, //IDirectorySearch pointer to schema naming context.
        ADS_SYSTEMFLAG_ATTR_IS_CONSTRUCTED, //Bit flags to search for in systemFlags
        NULL,
        FALSE
        );
 
    //Find indexed attributes
    wprintf(L"Find indexed attributes\n");
    hr = FindIndexedAttributes(pSchemaNC, //IDirectorySearch pointer to schema naming context.
        NULL,
        TRUE
        );
    }
    if (pSchemaNC)
        pSchemaNC->Release();
    }
}
if (pObject)
    pObject->Release();
VariantClear(&var);
 
// Uninitialize COM
CoUninitialize();
return 0;
}
 
HRESULT FindAttributesByType(IDirectorySearch *pSchemaNC, //IDirectorySearch pointer to schema naming context.
        DWORD dwAttributeType, //Bit flags to search for in systemFlags
        LPOLESTR pszAttributeNameType, //ldapDisplayName of the naming attribute you want to display for each returned attribute.
                                         //NULL returns common name.
        BOOL bIsExactMatch //TRUE to find attributes that have systemFlags exactly matching dwAttributeType
                            //FALSE to find attributes that have the dwAttributeType bit set (and possibly others).
        )
{
    //Create search filter
    LPOLESTR pszSearchFilter = new OLECHAR[MAX_PATH*2];
    if (bIsExactMatch)
        //Find attributes with systemFlags that exactly match dwAttributeType
        wsprintf(pszSearchFilter, L"(&(objectCategory=attributeSchema)(systemFlags=%d))",dwAttributeType);
    else
        //Find attributes with systemFlags that contain dwAttributeType
        wsprintf(pszSearchFilter, L"(&(objectCategory=attributeSchema)(systemFlags:1.2.840.113556.1.4.804:=%d))",dwAttributeType);
 
    //Attributes are one-level deep in the Schema container so only need to search one level.
    ADS_SEARCHPREF_INFO SearchPrefs;
    SearchPrefs.dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
    SearchPrefs.vValue.dwType = ADSTYPE_INTEGER;
    SearchPrefs.vValue.Integer = ADS_SCOPE_ONELEVEL;
    DWORD dwNumPrefs = 1;
 
    // COL for iterations
        ADS_SEARCH_COLUMN col;
        HRESULT hr;
        
        // Interface Pointers
        IADs    *pObj = NULL;
        IADs    * pIADs = NULL;
 
    // Handle used for searching
    ADS_SEARCH_HANDLE hSearch;
 
    // Set the search preference
        hr = pSchemaNC->SetSearchPreference( &SearchPrefs, dwNumPrefs);
        if (FAILED(hr))
            return hr;
 
    CONST DWORD dwAttrNameSize = 1;
        LPOLESTR pszAttribute[dwAttrNameSize];
        if (!pszAttributeNameType)
            pszAttribute[0] = L"cn";
        else
            pszAttribute[0] = pszAttributeNameType;
    // Execute the search
    hr = pSchemaNC->ExecuteSearch(pszSearchFilter,
                                  pszAttribute,
                                    dwAttrNameSize,
                                    &hSearch
                                    );
    if ( SUCCEEDED(hr) )
    {    
    // Call IDirectorySearch::GetNextRow() to retrieve the next row 
    //of data
        while( pSchemaNC->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 = pSchemaNC->GetColumn( hSearch, pszAttribute[x], &col );
                if ( SUCCEEDED(hr) )
                {
                    // Print the data for the column and free the column
                    wprintf(L"%s: %s\r\n",pszAttribute[x],col.pADsValues->CaseIgnoreString); 
                    pSchemaNC->FreeColumn( &col );
                }
                else
                    wprintf(L"<%s property is not a string>",pszAttribute[x]);
            }
        }
        // Close the search handle to clean up
        pSchemaNC->CloseSearchHandle(hSearch);
    } 
    return hr;
}
 
HRESULT FindIndexedAttributes(IDirectorySearch *pSchemaNC, //IDirectorySearch pointer to schema naming context.
        LPOLESTR pszAttributeNameType, //ldapDisplayName of the naming attribute you want to display for each returned attribute.
                                         //NULL returns common name.
        BOOL bIsIndexed //TRUE to find indexed attributes.
                         //FALSE to find non-indexed attributes.
        )
{
    //Create search filter
    LPOLESTR pszSearchFilter = new OLECHAR[MAX_PATH*2];
    DWORD dwIndexed;
    if (bIsIndexed)
        dwIndexed = 1;
    else
         dwIndexed = 0;
    
    wsprintf(pszSearchFilter, L"(&(objectCategory=attributeSchema)(searchFlags=%d))",dwIndexed);
 
    //Attributes are one-level deep in the Schema container so only need to search one level.
    ADS_SEARCHPREF_INFO SearchPrefs;
    SearchPrefs.dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
    SearchPrefs.vValue.dwType = ADSTYPE_INTEGER;
    SearchPrefs.vValue.Integer = ADS_SCOPE_ONELEVEL;
    DWORD dwNumPrefs = 1;
 
    // COL for iterations
    ADS_SEARCH_COLUMN col;
    HRESULT hr;
    
    // Interface Pointers
    IADs    *pObj = NULL;
    IADs    * pIADs = NULL;
 
    // Handle used for searching
    ADS_SEARCH_HANDLE hSearch;
 
    // Set the search preference
    hr = pSchemaNC->SetSearchPreference( &SearchPrefs, dwNumPrefs);
    if (FAILED(hr))
        return hr;
 
    CONST DWORD dwAttrNameSize = 1;
    LPOLESTR pszAttribute[dwAttrNameSize];
 
    if (!pszAttributeNameType)
        pszAttribute[0] = L"cn";
    else
        pszAttribute[0] = pszAttributeNameType;
 
    // Execute the search
    hr = pSchemaNC->ExecuteSearch(pszSearchFilter,
                                  pszAttribute,
                                    dwAttrNameSize,
                                    &hSearch
                                    );
    if ( SUCCEEDED(hr) )
    {    
    // Call IDirectorySearch::GetNextRow() to retrieve the next row 
    //of data
        while( pSchemaNC->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 = pSchemaNC->GetColumn( hSearch, pszAttribute[x], &col );
                if ( SUCCEEDED(hr) )
                {
                    // Print the data for the column and free the column
                    wprintf(L"%s: %s\r\n",pszAttribute[x],col.pADsValues->CaseIgnoreString); 
                    pSchemaNC->FreeColumn( &col );
                }
                else
                    wprintf(L"<%s property is not a string>",pszAttribute[x]);
            }
        }
        // Close the search handle to clean up
        pSchemaNC->CloseSearchHandle(hSearch);
    } 
    return hr;
}
 
HRESULT FindGCAttributes(IDirectorySearch *pSchemaNC, //IDirectorySearch pointer to schema naming context.
        LPOLESTR pszAttributeNameType, //ldapDisplayName of the naming attribute you want to display for each returned attribute.
                                         //NULL returns common name.
        BOOL bInGC //TRUE to find GC attributes.
                         //FALSE to find non-GC attributes.
        )
{
    //Create search filter
    LPOLESTR pszSearchFilter = new OLECHAR[MAX_PATH*2];
    LPOLESTR szBool = NULL;
    if (bInGC)
        szBool = L"TRUE";
    else
        szBool = L"FALSE";
    
    wsprintf(pszSearchFilter, L"(&(objectCategory=attributeSchema)(isMemberOfPartialAttributeSet=%s))",szBool);
    //Attributes are one-level deep in the Schema container so only need to search one level.
    ADS_SEARCHPREF_INFO SearchPrefs;
    SearchPrefs.dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
    SearchPrefs.vValue.dwType = ADSTYPE_INTEGER;
    SearchPrefs.vValue.Integer = ADS_SCOPE_ONELEVEL;
    DWORD dwNumPrefs = 1;
    // COL for iterations
    ADS_SEARCH_COLUMN col;
    HRESULT hr;
    // Interface Pointers
    IADs    *pObj = NULL;
    IADs    * pIADs = NULL;
    // Handle used for searching
    ADS_SEARCH_HANDLE hSearch;
    // Set the search preference
    hr = pSchemaNC->SetSearchPreference( &SearchPrefs, dwNumPrefs);
    if (FAILED(hr))
        return hr;
 
    CONST DWORD dwAttrNameSize = 1;
    LPOLESTR pszAttribute[dwAttrNameSize];
 
    if (!pszAttributeNameType)
        pszAttribute[0] = L"cn";
    else
        pszAttribute[0] = pszAttributeNameType;
 
    // Execute the search
    hr = pSchemaNC->ExecuteSearch(pszSearchFilter,
                                  pszAttribute,
                                  dwAttrNameSize,
                                  &hSearch
                                  );
    if ( SUCCEEDED(hr) )
    {    
    // Call IDirectorySearch::GetNextRow() to retrieve the next row 
    //of data
        while( pSchemaNC->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 = pSchemaNC->GetColumn( hSearch, pszAttribute[x], &col );
                if ( SUCCEEDED(hr) )
                {
                    // Print the data for the column and free the column
                    wprintf(L"%s: %s\r\n",pszAttribute[x],col.pADsValues->CaseIgnoreString); 
                    pSchemaNC->FreeColumn( &col );
                }
                else
                    wprintf(L"<%s property is not a string>",pszAttribute[x]);
            }
        }
 
        // Close the search handle to clean up
        pSchemaNC->CloseSearchHandle(hSearch);
    } 
    return hr;
}