Platform SDK: Active Directory, ADSI, and Directory Services

Example Code for Reading Properties

The following code fragments enumerate the properties of the specified user in the current domain, by searching for the user and then using IADsPropertyList to enumerate its properties (note how time/date values as large integers are handled and how octet strings for objectSID and objectGUID are handled.).

[C++]
//For the pow function to calculate powers of 2
#include <math.h>
#include <wchar.h>
#include <objbase.h>
#include <activeds.h>
 
//Make sure you define UNICODE
//Need to define version 5 for Windows 2000
#define _WIN32_WINNT 0x0500
//For SID conversion API.
#include <sddl.h>
 
 
HRESULT GetUserProperties(IADs * pObj);
 
HRESULT EnumeratePropertyValue(
        IADsPropertyEntry *pEntry
        );
 
HRESULT FindUserByName(IDirectorySearch *pSearchBase, //Container to search
                LPOLESTR szFindUser, //Name of user to find.
                IADs **ppUser); //Return a pointer to the user
 
 
void wmain( int argc, wchar_t *argv[ ])
{
 
//Handle the command line arguments.
LPOLESTR pszBuffer = new OLECHAR[MAX_PATH*2];
if (argv[1] == NULL)
{
    wprintf(L"This program finds a user in the current Window 2000 domain\n");
    wprintf(L"and displays its properties.\n");
    wprintf(L"Enter Common Name of the user to find:");
    _getws(pszBuffer);
}
else
    wcscpy(pszBuffer, argv[1]);
//if empty string, exit.
if (0==wcscmp(L"", pszBuffer))
    return;
 
wprintf(L"\nFinding user: %s...\n",pszBuffer);
 
//Intialize COM
CoInitialize(NULL);
HRESULT hr = S_OK;
//Get rootDSE and the domain container's DN.
IADs *pObject = NULL;
IDirectorySearch *pDS = NULL;
LPOLESTR szPath = new OLECHAR[MAX_PATH];
VARIANT var;
hr = ADsOpenObject(L"LDAP://rootDSE",
            NULL,
            NULL,
            ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
            IID_IADs,
            (void**)&pObject);
if (FAILED(hr))
{
    wprintf(L"Not Found. Could not bind to the domain.\n");
    if (pObject)
        pObject->Release();
    return;
}
 
hr = pObject->Get(L"defaultNamingContext",&var);
if (SUCCEEDED(hr))
{
    wcscpy(szPath,L"LDAP://");
    wcscat(szPath,var.bstrVal);
    VariantClear(&var);
    if (pObject)
    {
        pObject->Release();
        pObject = NULL;
    }
    //Bind to the root of the current domain.
    hr = ADsOpenObject(szPath,
                NULL,
                NULL,
                ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
                IID_IDirectorySearch,
                (void**)&pDS);
    if (SUCCEEDED(hr))
    {
        hr =  FindUserByName(pDS, //Container to search
                    pszBuffer, //Name of user to find.
                    &pObject); //Return a pointer to the user
        if (SUCCEEDED(hr))
        {
            wprintf (L"----------------------------------------------\n");
            wprintf (L"--------Call GetUserProperties-----------\n");
            hr = GetUserProperties(pObject);
            wprintf (L"GetUserProperties HR: %x\n", hr);
        }
        else
        {
            wprintf(L"User \"%s\" not Found.\n",pszBuffer);
            wprintf (L"FindUserByName failed with the following HR: %x\n", hr);
        }
        if (pObject)
            pObject->Release();
    }
 
    if (pDS)
        pDS->Release();
}
 
//uninitialize COM
CoUninitialize();
 
    return;
}
 
HRESULT GetUserProperties(IADs * pObj)
{
    HRESULT hr = E_FAIL;
    LPOLESTR szDSPath = new OLECHAR [MAX_PATH];
    IADsPropertyList *pObjProps = NULL;
    IADsPropertyEntry *pEntry = NULL;
    VARIANT var;
    BSTR szString;
    long lCount = 0L;
    long lCountTotal = 0L;
    long lPType = 0L;
    if (!pObj)
        return E_INVALIDARG;
    //Call GetInfo to load all properties for the object into the cache.
    //Must do this because IADsPropertyList methods read from the cache.
    hr = pObj->GetInfo();
    if (SUCCEEDED(hr))
    {
        //QI for an IADsPropertyList pointer.
        hr = pObj->QueryInterface(IID_IADsPropertyList, (void**)&pObjProps);
        if (SUCCEEDED(hr))
        {
            //Enumerate the properties of the object
            hr = pObjProps->get_PropertyCount(&lCountTotal);
            wprintf(L"Property Count: %d\n",lCountTotal);
            hr = pObjProps->Next(&var);
            if (SUCCEEDED(hr))
            {
                lCount = 1L;
                while (hr==S_OK)
                {
                    if (var.vt==VT_DISPATCH)
                    {
                        hr = V_DISPATCH(&var)->QueryInterface(IID_IADsPropertyEntry,  
(void**)&pEntry);
                        if (SUCCEEDED(hr))
                        {
                            hr = pEntry->get_Name(&szString);
                            wprintf(L"%s: ",szString);
                            SysFreeString(szString);
                            hr = pEntry->get_ADsType(&lPType);
                            if (lPType != ADSTYPE_INVALID)
                            {
                                hr = EnumeratePropertyValue(pEntry);
                                if(FAILED(hr))
                                    printf("EnumeratePropertyValue failed. hr:  
%x\n",hr);
                            }
                            else
                            {
                            wprintf(L"Invalid type\n");
                            }
                            }
                            else
                            {
                            printf("IADsPropertyEntry QueryInterface call failed. hr:  
%x\n",hr);
                        }
                        //Clean up
                        if (pEntry)
                            pEntry->Release();
                    }
                    else
                    {
                        printf("Unexpected returned type for VARIANT: %d",var.vt);
                    }
                    VariantClear(&var);
                    hr = pObjProps->Next(&var);
                    if (SUCCEEDED(hr))
                    {
                        lCount++;
                    }
                }
            }
        }
        wprintf(L"Total properties retrieved: %d\n",lCount); 
        //Clean up
        if (pObjProps)
            pObjProps->Release();
    }
    //Return success if all properties were retrieved.
    if (lCountTotal==lCount)
        hr=S_OK;
    return hr;
}
 
 
HRESULT EnumeratePropertyValue(
          IADsPropertyEntry *pEntry
          )
{
HRESULT hr = E_FAIL;
IADsPropertyValue *pValue = NULL;
IADsLargeInteger *pLargeInt = NULL;
long lType, lValue;
BSTR bstr,szString;
VARIANT var,varOS,varDate;
VARIANT *pVar;
CHAR *pszBOOL = NULL;
 
FILETIME filetime;
SYSTEMTIME systemtime;
IDispatch *pDisp = NULL;
DATE date;
 
//For Octet Strings
void HUGEP *pArray;
ULONG dwSLBound;
ULONG dwSUBound;
 
 
 
 
hr = pEntry->get_Values(&var);
if (SUCCEEDED(hr))
{
    //Should be safe array containing variants
    if (var.vt==(VT_VARIANT|VT_ARRAY))
    {
        hr = SafeArrayAccessData((SAFEARRAY*)(var.pparray), (void HUGEP* FAR*)&pVar);
        long lLBound, lUBound;
        //One dimensional array. Get the bounds for the array.
        hr = SafeArrayGetLBound((SAFEARRAY*)(var.pparray), 1, &lLBound);
        hr = SafeArrayGetUBound((SAFEARRAY*)(var.pparray), 1, &lUBound);
        //Get the count of elements
        long cElements = lUBound-lLBound + 1;
        //Get the elements of the array
        if (SUCCEEDED(hr))
        {
            for (int i = 0; i < cElements; i++ ) 
            {
                switch (pVar[i].vt)
                {
                case VT_BSTR:
                    wprintf(L"%s ",pVar[i].bstrVal);
                    break;
                case VT_DISPATCH:
                    hr = V_DISPATCH(&pVar[i])->QueryInterface(IID_IADsPropertyValue, (void**)&pValue);
                    if (SUCCEEDED(hr))
                    {
                    hr = pValue->get_ADsType(&lType);
                    switch (lType)
                    {
                    case ADSTYPE_DN_STRING:
                        hr = pValue->get_DNString(&bstr);
                        wprintf(L"%s ",bstr);
                        SysFreeString(bstr);
                        break;
                    case ADSTYPE_CASE_IGNORE_STRING:
                        hr = pValue->get_CaseIgnoreString(&bstr);
                        wprintf(L"%s ",bstr);
                        SysFreeString(bstr);
                        break;
                    case ADSTYPE_BOOLEAN:
                        hr = pValue->get_Boolean(&lValue);
                        pszBOOL = lValue ? "TRUE" : "FALSE";
                        wprintf(L"%s ",pszBOOL);
                        break;
                    case ADSTYPE_INTEGER:
                        hr = pValue->get_Integer(&lValue);
                        wprintf(L"%d ",lValue);
                        break;
                    case ADSTYPE_OCTET_STRING:
                        //Get the name of the property to handle
                        //the properties we're interested in.
                        pEntry->get_Name(&szString);
                        hr = pValue->get_OctetString(&varOS);
                        //Get a pointer to the bytes in the octet string.
                        if (SUCCEEDED(hr))
                        {
                            hr = SafeArrayGetLBound( V_ARRAY(&varOS),
                                                        1,
                                                        (long FAR  
*) &dwSLBound );
                            hr = SafeArrayGetUBound( V_ARRAY(&varOS),
                                                        1,
                                                        (long FAR  
*) &dwSUBound );
                            if (SUCCEEDED(hr))
                            {
                                hr = SafeArrayAccessData( V_ARRAY(&varOS),
  
&pArray );
                            }
                            if (0==wcscmp(L"objectGUID", szString))
                            {
                                LPOLESTR szDSGUID = new WCHAR [39];
                                //Cast to LPGUID
                                LPGUID pObjectGUID = (LPGUID)pArray;
                                //Convert GUID to string.
                                ::StringFromGUID2(*pObjectGUID, szDSGUID, 39); 
                                //Print the GUID
                                wprintf(L"%s ",szDSGUID);
                            }
                            else if (0==wcscmp(L"objectSid", szString))
                            {
                                PSID pObjectSID = (PSID)pArray;
                                //Convert SID to string.
                                LPOLESTR szSID = NULL;
                                ConvertSidToStringSid(pObjectSID, &szSID);
                                wprintf(L"%s ",szSID);
                                LocalFree(szSID);
                            }
                            else
                            {
                                wprintf(L"Value of type Octet String. No  
Conversion.");
                            }
                                SafeArrayUnaccessData( V_ARRAY(&varOS) );
                                VariantClear(&varOS);
                        }
 
                        SysFreeString(szString);
 
                        break;
                    case ADSTYPE_UTC_TIME:
                        //wprintf(L"Value of type UTC_TIME\n");
                        hr = pValue->get_UTCTime(&date);
                        if (SUCCEEDED(hr)) 
                        {
                            //Pack in variant.vt
                            varDate.vt = VT_DATE;
                            varDate.date = date;
                             
VariantChangeType(&varDate,&varDate,VARIANT_NOVALUEPROP,VT_BSTR);
                            wprintf(L"%s ",varDate.bstrVal);
                            VariantClear(&varDate);
                        }
                        break;
                    case ADSTYPE_LARGE_INTEGER:
                        //wprintf(L"Value of type Large Integer\n");
                        //Get the name of the property to handle
                        //the properties we're interested in.
                        pEntry->get_Name(&szString);
                        hr = pValue->get_LargeInteger(&pDisp);
                        if (SUCCEEDED(hr))
                        {
                            hr = pDisp->QueryInterface(IID_IADsLargeInteger,  
(void**)&pLargeInt);
                            if (SUCCEEDED(hr))
                            {
                                hr =  
pLargeInt->get_HighPart((long*)&filetime.dwHighDateTime);
                                hr =  
pLargeInt->get_LowPart((long*)&filetime.dwLowDateTime);
                                if((filetime.dwHighDateTime==0) &&  
(filetime.dwLowDateTime==0))
                                {
                                    wprintf(L"No Value ");
                                }
                                else
                                {
                                    //Check for properties of type LargeInteger  
that represent time
                                    //if TRUE, then convert to variant time.
                                    if ((0==wcscmp(L"accountExpires", szString))|
                                        (0==wcscmp(L"badPasswordTime",  
szString))||
                                        (0==wcscmp(L"lastLogon", szString))||
                                        (0==wcscmp(L"lastLogoff",  
szString))||
                                        (0==wcscmp(L"lockoutTime",  
szString))||
                                        (0==wcscmp(L"pwdLastSet", szString))
                                        )
                                    {
                                        //Handle special case for Never  
Expires where low part is -1
                                        if (filetime.dwLowDateTime==-1)
                                        {
                                            wprintf(L"Never Expires ");
                                        }
                                        else
                                        {
                                            if  
(FileTimeToLocalFileTime(&filetime, &filetime) != 0) 
                                            {
                                                if  
(FileTimeToSystemTime(&filetime,
  
        &systemtime) != 0)
                                                {
                                                    if  
(SystemTimeToVariantTime(&systemtime,
  
                    &date) != 0) 
                                                    {
  
//Pack in variant.vt
  
varDate.vt = VT_DATE;
  
varDate.date = date;
  
VariantChangeType(&varDate,&varDate,VARIANT_NOVALUEPROP,VT_BSTR);
  
wprintf(L"%s ",varDate.bstrVal);
  
VariantClear(&varDate);
                                                    }
                                                    else
                                                    {
  
wprintf(L"FileTimeToVariantTime failed ");
                                                    }
                                                }
                                                else
                                                {
  
wprintf(L"FileTimeToSystemTime failed ");
                                                }
 
                                            }
                                            else
                                            {
  
wprintf(L"FileTimeToLocalFileTime failed ");
                                            }
                                        }
                                    }
                                    //Print the LargeInteger.
                                    else
                                    {
                                        wprintf(L"Large Integer: high: %d  
low: %d ",filetime.dwHighDateTime, filetime.dwLowDateTime);
                                    }
                                }
                            }
                            if (pLargeInt)
                                pLargeInt->Release();
                        }
                        else
                        {
                            wprintf(L"Could not get Large Integer");
                        }
 
                        if (pDisp)
                            pDisp->Release();
 
                        break;
                    case ADSTYPE_NT_SECURITY_DESCRIPTOR:
                        wprintf(L"Value of type NT Security Descriptor ");
                        break;
                    case ADSTYPE_PROV_SPECIFIC:
                        wprintf(L"Value of type Provider Specific ");
                        break;
                    default:
                        wprintf(L"Unhandled ADSTYPE for property value: %d ",lType);
                        break;
                    }
                    }
                    else
                    {
                        wprintf(L"QueryInterface failed for IADsPropertyValue. HR: %x\n", hr);
                    }
                    if (pValue)
                        pValue->Release();
                    break;
                default:
                    wprintf(L"Unhandled Variant type for property value array: %d\n",pVar[i].vt);
                    break;
                }
            }
            wprintf(L"\n");
        }
        //Decrement the access count for the array.
        SafeArrayUnaccessData((SAFEARRAY*)(var.pparray));
            
    }
}
 
 
    return hr;
}
 
 
HRESULT FindUserByName(IDirectorySearch *pSearchBase, //Container to search
                        LPOLESTR szFindUser, //Name of user to find.
                        IADs **ppUser) //Return a pointer to the user
{
    HRESULT hrObj = E_FAIL;
    HRESULT hr = E_FAIL;
    if ((!pSearchBase)||(!szFindUser))
        return E_INVALIDARG;
    //Create search filter
    LPOLESTR pszSearchFilter = new OLECHAR[MAX_PATH];
    LPOLESTR szADsPath = new OLECHAR[MAX_PATH];
    wcscpy(pszSearchFilter, L"(&(objectCategory=person)(objectClass=user)(cn=");
    wcscat(pszSearchFilter, szFindUser);
    wcscat(pszSearchFilter,    L"))");
        //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 = pSearchBase->SetSearchPreference( &SearchPrefs, dwNumPrefs);
        if (FAILED(hr))
            return hr;
    // Set attributes to return
    CONST DWORD dwAttrNameSize = 1;
        LPOLESTR pszAttribute[dwAttrNameSize] = {L"ADsPath"};
 
        // Execute the search
        hr = pSearchBase->ExecuteSearch(pszSearchFilter,
                                pszAttribute,
                                    dwAttrNameSize,
                                    &hSearch
                                );
    if (SUCCEEDED(hr))
    {    
 
    // Call IDirectorySearch::GetNextRow() to retrieve the next row 
    //of data
        while( pSearchBase->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 = pSearchBase->GetColumn( hSearch, pszAttribute[x], &col );
                if ( SUCCEEDED(hr) )
                {
                    // Print the data for the column and free the column
                    // Note the requested attribute is type CaseIgnoreString.
                    wcscpy(szADsPath, col.pADsValues->CaseIgnoreString); 
                    hr = ADsOpenObject(szADsPath,
                                    NULL,
                                    NULL,
                                    ADS_SECURE_AUTHENTICATION, //Use Secure  
Authentication
                                    IID_IADs,
                                    (void**)ppUser);
                    if (SUCCEEDED(hr))
                    {
                       wprintf(L"Found User.\n",szFindUser); 
                       wprintf(L"%s: %s\r\n",pszAttribute[x],col.pADsValues->CaseIgnoreString); 
                        hrObj = S_OK;
                    }
                    pSearchBase->FreeColumn( &col );
                }
                else
                    hr = E_FAIL;
            }
        }
        // Close the search handle to clean up
        pSearchBase->CloseSearchHandle(hSearch);
    }
    if (FAILED(hrObj))
        hr = hrObj;
    return hr;
}
[Visual Basic]
Dim sUserName As String
    Dim sSearchFilter As String
    Dim lScope As Integer
    Dim iIndex As Integer
    iIndex = 0
    Dim v, j, i
    Dim ds As IADs
    Dim con As New Connection, rs As New Recordset
    Dim Com As New Command
    Dim oIADs As IADs
    Dim sLdap As String
    Dim sUserADsPath As String
    
    sUserName = InputBox("This script Enumerates the properties of a user on a domain." & vbCrLf & vbCrLf & "Specify the name of the user:")

    If sUserName = "" Then
        Exit Sub
    End If
        
    ' Bind to the rootDSE
    Set ds = GetObject("LDAP://RootDSE")
    sLdap = "LDAP://" & ds.Get("defaultNamingContext")
    Set ds = Nothing
    
    Set oIADs = GetObject(sLdap)
    
    sSearchFilter = "CN='" & sUserName & "'"
    
    '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
    
    ' set the query string using SQL Dialect
    Com.CommandText = "select name,AdsPath from '" & oIADs.ADsPath & "' where " & sSearchFilter & " ORDER BY NAME"
    
    ' Tell the user what the search filter is
    'MsgBox "Search Filter = " & Com.CommandText
    
    '---------------------------------------------------
    ' Or you can use LDAP Dialect, for example,
    '---------------------------------------------------
    ' Ex Com.CommandText = "<LDAP://ntdsdc1/dc=NTDEV,DC=microsof,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_SUBTREE
    Com.Properties("Chase referrals") = ADS_CHASE_REFERRALS_EXTERNAL
    Com.Properties("Cache Results") = False ' do not cache the result, it results in less memory requirements
    Com.Properties("Size Limit") = 1 ' Limit to 1 Result

    'Execute the query
    Set rs = Com.Execute
    
    ' Navigate the record set
     If Not rs.EOF Then
        rs.MoveFirst
    End If
  
    On Error Resume Next
    If Not rs.EOF Then
        ' Display the LDAP path for the row
        MsgBox "Found the user " & sUserName & " at " & rs.Fields("AdsPath")
        sUserADsPath = rs.Fields("AdsPath")
        rs.MoveNext
    Else
        MsgBox "Did not find the username " & sUserName & " in the directory"
        Exit Sub
    End If
    
    Set ds = Nothing
    Set con = Nothing
    Set rs = Nothing
    Set Com = Nothing
    Set oIADs = Nothing
    
    ' Now enumerate the properties
    Dim propList As IADsPropertyList
    Dim propEnty As IADsPropertyEntry
    Dim propVal As IADsPropertyValue
    Dim count As Long
    
    Dim sOutput As String
    Dim currentcount As Long

    Const NumToDisplayAtAtime As Integer = 10
    
    ' Bind to the user
    Set propList = GetObject(sUserADsPath)
    
    ' Bring the properties into the cache
    propList.GetInfo
    
    count = propList.PropertyCount
    sOutput = "No of Property Found: " & Str(count) & vbCrLf & vbCrLf
    
    For i = 0 To count - 1
        currentcount = currentcount + 1
        'Each item in property list has a property entry
        Set propEntry = propList.Item(i)
        
        ' Append to outputstring
        sOutput = sOutput & "PROPERTYENTRY NAME:" & propEntry.Name & vbCrLf & " ------" & vbCrLf
       
        'Each value in property entry has property values
        For Each v In propEntry.Values
            Set propVal = v
            ' Append to outputstring
            sOutput = sOutput & propVal.CaseIgnoreString vbCrLf
        Next
        If currentcount = NumToDisplayAtAtime Then
            MsgBox sOutput
            sOutput = ""
            currentcount = 0
        End If
    Next
    Set propList = Nothing
    Set propEnty = Nothing
    Set propVal = Nothing