| Platform SDK: Active Directory, ADSI, and Directory Services |
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.).
//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;
}
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