Пример кода для поиска пользователей
В следующем примере кода выполняется поиск пользователей в домене учетной записи пользователя, в которой выполняется вызывающий процесс.
Пример C++
В следующем примере кода C++ выполняется поиск текущего домена для всех пользователей или конкретного пользователя на основе указанного фильтра.
// Add msvcrt.dll to the project.
// Add activeds.lib to the project.
#include "stdafx.h"
#include <objbase.h>
#include <wchar.h>
#include <activeds.h>
// Define UNICODE.
// Define version 5 for Windows 2000.
#define _WIN32_WINNT 0x0500
#include <sddl.h>
HRESULT FindUsers(IDirectorySearch *pContainerToSearch, // IDirectorySearch pointer to the container to search.
LPOLESTR szFilter, // Filter to find specific users.
// NULL returns all user objects.
LPOLESTR *pszPropertiesToReturn, // Properties to return for user objects found.
// NULL returns all set properties.
BOOL bIsVerbose // TRUE indicates that display all properties for the found objects.
// FALSE indicates that only the RDN.
);
// Entry point for the application.
void wmain(int argc, wchar_t *argv[ ])
{
#ifdef _MBCS
// Handle the command line arguments.
DWORD dwLength = MAX_PATH*2;
LPOLESTR pszBuffer = new OLECHAR[dwLength];
wcsncpy_s(pszBuffer, L"",dwLength);
BOOL bReturnVerbose = FALSE;
for (int i = 1;i<argc;i++)
{
if (_wcsicmp(argv[i],L"/V") == 0)
{
bReturnVerbose = TRUE;
}
else if ((_wcsicmp(argv[i],L"/?") == 0)||
(_wcsicmp(argv[i],L"-?") == 0))
{
wprintf(L"This application queries for users in the current user domain.\n");
wprintf(L"Syntax: queryusers [/V][querystring]\n");
wprintf(L"where /V specifies that all properties for the found users should be returned.\n");
wprintf(L"querystring is the query criteria in ldap query format.\n");
wprintf(L"Defaults: If no /V is specified, the query returns only the RDN and DN of the items found.\n");
wprintf(L"If no querystring is specified, the query returns all users.\n");
wprintf(L"Example: queryusers (sn=Smith)\n");
wprintf(L"Returns all users with surname Smith.\n");
return;
}
else
{
wcsncpy_s(pszBuffer,argv[i],dwLength-wcslen(pszBuffer));
}
}
if (_wcsicmp(pszBuffer,L"") == 0)
wprintf(L"\nFinding all user objects...\n\n");
else
wprintf(L"\nFinding user objects based on query: %s...\n\n", pszBuffer);
// Initialize COM.
CoInitialize(NULL);
HRESULT hr = S_OK;
// Get rootDSE and the current user domain container distinguished name.
IADs *pObject = NULL;
IDirectorySearch *pContainerToSearch = 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"Cannot execute query. Cannot bind to LDAP://rootDSE.\n");
if (pObject)
pObject->Release();
return;
}
if (SUCCEEDED(hr))
{
hr = pObject->Get(_bstr_t("defaultNamingContext"),&var);
if (SUCCEEDED(hr))
{
// Build path to the domain container.
wcsncpy_s(szPath,L"LDAP://",MAX_PATH);
wcsncat_s(szPath,var.bstrVal,MAX_PATH-wcslen(szPath));
hr = ADsOpenObject(szPath,
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, // Use Secure Authentication.
IID_IDirectorySearch,
(void**)&pContainerToSearch);
if (SUCCEEDED(hr))
{
hr = FindUsers(pContainerToSearch, // IDirectorySearch pointer to domainDNS container.
pszBuffer,
NULL, // Return all properties.
bReturnVerbose
);
if (SUCCEEDED(hr))
{
if (S_FALSE==hr)
wprintf(L"User object cannot be found.\n");
}
else if (E_ADS_INVALID_FILTER==hr)
wprintf(L"Cannot execute query. Invalid filter was specified.\n");
else
wprintf(L"Query failed to run. HRESULT: %x\n",hr);
}
else
{
wprintf(L"Cannot execute query. Cannot bind to the container.\n");
}
if (pContainerToSearch)
pContainerToSearch->Release();
}
VariantClear(&var);
}
if (pObject)
pObject->Release();
// Uninitialize COM.
CoUninitialize();
delete [] szPath;
delete [] pszBuffer;
return;
#endif _MBCS
}
HRESULT FindUsers(IDirectorySearch *pContainerToSearch, // IDirectorySearch pointer to the container to search.
LPOLESTR szFilter, // Filter for finding specific users.
// NULL returns all user objects.
LPOLESTR *pszPropertiesToReturn, // Properties to return for user objects found.
// NULL returns all set properties.
BOOL bIsVerbose // TRUE indicates that all properties for the found objects are displayed.
// FALSE indicates only the RDN.
)
{
if (!pContainerToSearch)
return E_POINTER;
DWORD dwLength = MAX_PATH*2;
// Create search filter.
LPOLESTR pszSearchFilter = new OLECHAR[dwLength];
// Add the filter.
swprintf_s(pszSearchFilter, dwLength, L"(&(objectClass=user)(objectCategory=person)%s)",szFilter);
// Specify subtree search.
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.
LPOLESTR pszColumn = NULL;
ADS_SEARCH_COLUMN col;
HRESULT hr = S_OK;
// Interface Pointers
IADs *pObj = NULL;
IADs * pIADs = NULL;
// Search handle.
ADS_SEARCH_HANDLE hSearch = NULL;
// Set search preference.
hr = pContainerToSearch->SetSearchPreference(&SearchPrefs, dwNumPrefs);
if (FAILED(hr))
return hr;
LPOLESTR pszBool = NULL;
DWORD dwBool = 0;
PSID pObjectSID = NULL;
LPOLESTR szSID = NULL;
LPOLESTR szDSGUID = new WCHAR [39];
LPGUID pObjectGUID = NULL;
FILETIME filetime;
SYSTEMTIME systemtime;
DATE date;
VARIANT varDate;
LARGE_INTEGER liValue;
LPOLESTR *pszPropertyList = NULL;
LPOLESTR pszNonVerboseList[] = {L"name",L"distinguishedName"};
LPOLESTR szName = new OLECHAR[MAX_PATH];
LPOLESTR szDN = new OLECHAR[MAX_PATH];
VariantInit(&varDate);
int iCount = 0;
DWORD x = 0L;
if (!bIsVerbose)
{
// Return non-verbose list properties only.
hr = pContainerToSearch->ExecuteSearch(pszSearchFilter,
pszNonVerboseList,
sizeof(pszNonVerboseList)/sizeof(LPOLESTR),
&hSearch
);
}
else
{
if (!pszPropertiesToReturn)
{
// Return all properties.
hr = pContainerToSearch->ExecuteSearch(pszSearchFilter,
NULL,
(DWORD)-1,
&hSearch
);
}
else
{
// Specified subset.
pszPropertyList = pszPropertiesToReturn;
// Return specified properties.
hr = pContainerToSearch->ExecuteSearch(pszSearchFilter,
pszPropertyList,
sizeof(pszPropertyList)/sizeof(LPOLESTR),
&hSearch
);
}
}
if (SUCCEEDED(hr))
{
// Call IDirectorySearch::GetNextRow() to retrieve the next data row.
hr = pContainerToSearch->GetFirstRow(hSearch);
if (SUCCEEDED(hr))
{
while(hr != S_ADS_NOMORE_ROWS)
{
// Keep track of count.
iCount++;
if (bIsVerbose)
wprintf(L"----------------------------------\n");
// Loop through the array of passed column names,
// print the data for each column.
while(pContainerToSearch->GetNextColumnName(hSearch, &pszColumn) != S_ADS_NOMORE_COLUMNS)
{
hr = pContainerToSearch->GetColumn(hSearch, pszColumn, &col);
if (SUCCEEDED(hr))
{
// Print the data for the column and free the column.
if(bIsVerbose)
{
// Get the data for this column.
wprintf(L"%s\n",col.pszAttrName);
switch (col.dwADsType)
{
case ADSTYPE_DN_STRING:
for (x = 0; x< col.dwNumValues; x++)
{
wprintf(L" %s\r\n",col.pADsValues[x].DNString);
}
break;
case ADSTYPE_CASE_EXACT_STRING:
case ADSTYPE_CASE_IGNORE_STRING:
case ADSTYPE_PRINTABLE_STRING:
case ADSTYPE_NUMERIC_STRING:
case ADSTYPE_TYPEDNAME:
case ADSTYPE_FAXNUMBER:
case ADSTYPE_PATH:
for (x = 0; x< col.dwNumValues; x++)
{
wprintf(L" %s\r\n",col.pADsValues[x].CaseIgnoreString);
}
break;
case ADSTYPE_BOOLEAN:
for (x = 0; x< col.dwNumValues; x++)
{
dwBool = col.pADsValues[x].Boolean;
pszBool = dwBool ? L"TRUE" : L"FALSE";
wprintf(L" %s\r\n",pszBool);
}
break;
case ADSTYPE_INTEGER:
for (x = 0; x< col.dwNumValues; x++)
{
wprintf(L" %d\r\n",col.pADsValues[x].Integer);
}
break;
case ADSTYPE_OCTET_STRING:
if (_wcsicmp(col.pszAttrName,L"objectSID") == 0)
{
for (x = 0; x< col.dwNumValues; x++)
{
pObjectSID = (PSID)(col.pADsValues[x].OctetString.lpValue);
// Convert SID to string.
ConvertSidToStringSid(pObjectSID, &szSID);
wprintf(L" %s\r\n",szSID);
LocalFree(szSID);
}
}
else if ((_wcsicmp(col.pszAttrName,L"objectGUID") == 0))
{
for (x = 0; x< col.dwNumValues; x++)
{
// Cast to LPGUID.
pObjectGUID = (LPGUID)(col.pADsValues[x].OctetString.lpValue);
// Convert GUID to string.
::StringFromGUID2(*pObjectGUID, szDSGUID, 39);
// Print the GUID.
wprintf(L" %s\r\n",szDSGUID);
}
}
else
wprintf(L" Value of type Octet String. No Conversion.");
break;
case ADSTYPE_UTC_TIME:
for (x = 0; x< col.dwNumValues; x++)
{
systemtime = col.pADsValues[x].UTCTime;
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\r\n",varDate.bstrVal);
VariantClear(&varDate);
}
else
wprintf(L" Could not convert UTC-Time.\n",pszColumn);
}
break;
case ADSTYPE_LARGE_INTEGER:
for (x = 0; x< col.dwNumValues; x++)
{
liValue = col.pADsValues[x].LargeInteger;
filetime.dwLowDateTime = liValue.LowPart;
filetime.dwHighDateTime = liValue.HighPart;
if((filetime.dwHighDateTime==0) && (filetime.dwLowDateTime==0))
{
wprintf(L" No value set.\n");
}
else
{
// Verify properties of type LargeInteger that represent time.
// If TRUE, then convert to variant time.
if ((0==wcscmp(L"accountExpires", col.pszAttrName))|
(0==wcscmp(L"badPasswordTime", col.pszAttrName))||
(0==wcscmp(L"lastLogon", col.pszAttrName))||
(0==wcscmp(L"lastLogoff", col.pszAttrName))||
(0==wcscmp(L"lockoutTime", col.pszAttrName))||
(0==wcscmp(L"pwdLastSet", col.pszAttrName))
)
{
// Handle special case for Never Expires where low part is -1.
if (filetime.dwLowDateTime==-1)
{
wprintf(L" Never Expires.\n");
}
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\r\n",varDate.bstrVal);
VariantClear(&varDate);
}
else
{
wprintf(L" FileTimeToVariantTime failed\n");
}
}
else
{
wprintf(L" FileTimeToSystemTime failed\n");
}
}
else
{
wprintf(L" FileTimeToLocalFileTime failed\n");
}
}
}
else
{
// Print the LargeInteger.
wprintf(L" high: %d low: %d\r\n",filetime.dwHighDateTime, filetime.dwLowDateTime);
}
}
}
break;
case ADSTYPE_NT_SECURITY_DESCRIPTOR:
for (x = 0; x< col.dwNumValues; x++)
{
wprintf(L" Security descriptor.\n");
}
break;
default:
wprintf(L"Unknown type %d.\n",col.dwADsType);
}
}
else
{
#ifdef _MBCS
// Verbose handles only the two single-valued attributes: cn and ldapdisplayname,
// so this is a special case.
if (0==wcscmp(L"name", pszColumn))
{
wcscpy_s(szName,col.pADsValues->CaseIgnoreString);
}
if (0==wcscmp(L"distinguishedName", pszColumn))
{
wcscpy_s(szDN,col.pADsValues->CaseIgnoreString);
}
#endif _MBCS
}
pContainerToSearch->FreeColumn(&col);
}
FreeADsMem(pszColumn);
}
if (!bIsVerbose)
wprintf(L"%s\n DN: %s\n\n",szName,szDN);
// Get the next row.
hr = pContainerToSearch->GetNextRow(hSearch);
}
}
// Close the search handle to cleanup.
pContainerToSearch->CloseSearchHandle(hSearch);
}
if (SUCCEEDED(hr) && 0==iCount)
hr = S_FALSE;
delete [] szName;
delete [] szDN;
delete [] szDSGUID;
delete [] pszSearchFilter;
return hr;
}
Пример Visual Basic
В следующем примере кода Visual Basic выполняется поиск текущего домена для пользователей с указанной фамилией и возвращается атрибуты name и отличительные атрибутыName для найденных объектов. В следующем примере кода для выполнения поиска используется 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
' Prompt for surname to search for.
strName = InputBox("This routine searches in the current domain for users with the specified surname." & vbCrLf & vbCrLf &"Specify the surname:")
If strName = "" Then
msgbox "No surname was specified. The routine will search for all users."
End If
' Create ADO connection object for Active Directory
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
' Create ADO command object for the connection.
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
' Get the ADsPath for the domain to search.
Set root = GetObject("LDAP://rootDSE")
If (Err.Number <> 0) Then
BailOnFailure Err.Number, "on GetObject for rootDSE"
End If
sDomain = root.Get("defaultNamingContext")
If (Err.Number <> 0) Then
BailOnFailure Err.Number, "on Get on defaultNamingContext"
End If
Set domain = GetObject("LDAP://" & sDomain)
If (Err.Number <> 0) Then
BailOnFailure Err.Number, "on GetObject for domain"
End If
' Build the ADsPath element of the commandtext
sADsPath = "<" & domain.ADsPath & ">"
' Build the filter element of the commandtext
If (strName = "") Then
sFilter = "(&(objectCategory=person)(objectClass=user))"
Else
sFilter = "(&(objectCategory=person)(objectClass=user)(sn=" & strName & "))"
End If
' Build the returned attributes element of the commandtext.
sAttribsToReturn = "name,distinguishedName"
' Build the depth element of the commandtext.
sDepth = "subTree"
' Assemble the commandtext.
ocommand.CommandText = sADsPath & ";" & sFilter & ";" & sAttribsToReturn & ";" & sDepth
If (Err.Number <> 0) Then
BailOnFailure Err.Number, "on CommandText"
End If
' Display.
show_items "CommandText: " & ocommand.CommandText, ""
' Execute the query.
Set rs = ocommand.Execute
If (Err.Number <> 0) Then
BailOnFailure Err.Number, "on Execute"
End If
strText = "Found " & rs.RecordCount & " Users in the domain:"
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 domain")
strText = ""
intNumDisplay = 0
End If
rs.MoveNext
Wend
show_items strText, "Users in domain"
''''''''''''''''''''
' Display subroutines
''''''''''''''''''''
Sub show_items(strText, strName)
MsgBox strText, vbInformation, "Search domain for users with Surname " & strName
End Sub
Sub BailOnFailure(ErrNum, ErrText) strText = "Error 0x" & Hex(ErrNum) & " " & ErrText
MsgBox strText, vbInformation, "ADSI Error"
WScript.Quit
End Sub