Directory Services

Performing an Attribute Scope Query

Beginning with Microsoft® Windows Server 2003 family operating systems, the attribute scope query is a new search preference that enables a search of an object's distinguished name valued attributes to be performed. The attribute to search can be either single or multi-valued, but must be of the ADS_DN_STRING type. When the search is performed, ADSI will enumerate the distinguished name values of the attribute and perform the search on the objects represented by the distinguished names. For example, if an attribute scoped search is performed of the member attribute of a group object, ADSI will enumerate the distinguished names in the member attribute and search each of the members of the group for the specified search criteria.

If an attribute scoped query is performed on an attribute that is not of type ADS_DN_STRING, the search will fail. The attribute scoped query also requires that the ADS_SEARCHPREF_SEARCH_SCOPE preference be set to ADS_SCOPE_BASE. The ADS_SEARCHPREF_SEARCH_SCOPE preference will automatically be set to ADS_SCOPE_BASE, but if the ADS_SEARCHPREF_SEARCH_SCOPE preference is set to any other value, IDirectorySearch::SetSearchPreference will fail with E_ADS_BAD_PARAMETER.

The results of an attribute-scope query may span multiple servers and a server may not return all the data requested for the all the rows returned. If this occurs, when the last row is retrieved by calling IDirectorySearch::GetNextRow or IDirectorySearch::GetFirstRow, ADSI will return S_ADS_ERRORSOCCURRED instead of S_ADS_NOMORE_ROWS.

To specify an attribute scope query, set an ADS_SEARCHPREF_ATTRIBUTE_QUERY search option with an ADSTYPE_CASE_IGNORE_STRING value set to the lDAPDisplayName of the attribute to search in the ADS_SEARCHPREF_INFO array passed to the IDirectorySearch::SetSearchPreference method. This operation is shown in the following code example.

[C++]
ADS_SEARCHPREF_INFO SearchPref;
SearchPref.dwSearchPref = ADS_SEARCHPREF_ATTRIBUTE_QUERY;
SearchPref.vValue.dwType = ADSTYPE_CASE_IGNORE_STRING;
SearchPref.vValue.Boolean = L"member";

The following code example shows how to use the ADS_SEARCHPREF_ATTRIBUTE_QUERY search option.

[C++]
/***************************************************************************

	SearchGroupMembers()

	Searches the members of a group that are of type user and prints each 
	user's cn and distinguishedName values to the console.

	Parameters:

	pwszGroupDN - Contains the disintinguished name of the group whose 
	members will be searched.

***************************************************************************/

HRESULT SearchGroupMembers(LPCWSTR pwszGroupDN)
{
	HRESULT hr;
	CComPtr<IDirectorySearch> spSearch;
	CComBSTR sbstrADsPath;
 
	// Bind to the group and get the IDirectorySearch interface.
	sbstrADsPath = "LDAP://";
	sbstrADsPath += pwszGroupDN;
	hr = ADsOpenObject(sbstrADsPath,
		NULL,
		NULL,
		ADS_SECURE_AUTHENTICATION,
		IID_IDirectorySearch,
		(void**)&spSearch);
	if(FAILED(hr))
	{
		return hr;
}
 
	ADS_SEARCHPREF_INFO SearchPrefs[1];

	// Set the ADS_SEARCHPREF_ATTRIBUTE_QUERY search preference.
	SearchPrefs[0].dwSearchPref = ADS_SEARCHPREF_ATTRIBUTE_QUERY;
	SearchPrefs[0].vValue.dwType = ADSTYPE_CASE_IGNORE_STRING;
	SearchPrefs[0].vValue.CaseIgnoreString = L"member";

	// Set the search preferences.
	hr = spSearch->SetSearchPreference(SearchPrefs, sizeof(SearchPrefs)/sizeof(ADS_SEARCHPREF_INFO));
	if(FAILED(hr))
	{
		return hr;
}

	ADS_SEARCH_HANDLE hSearch;

	// Create the search filter.
	LPWSTR pwszSearchFilter = L"(&(objectClass=user))";
 
	// Set attributes to return.
	LPWSTR rgpwszAttributes[] = {L"cn", L"distinguishedName"};
	DWORD dwNumAttributes = sizeof(rgpwszAttributes)/sizeof(LPWSTR);
 
	// Execute the search.
	hr = spSearch->ExecuteSearch(pwszSearchFilter,
		rgpwszAttributes,
		dwNumAttributes,
		&hSearch);
	if(FAILED(hr))
	{
		return hr;
}

	// Get the first result row.
	hr = spSearch->GetFirstRow(hSearch);
	while(S_OK == hr)
	{
		ADS_SEARCH_COLUMN col;

		// Enumerate the retrieved attributes.
		for(DWORD i = 0; i < dwNumAttributes; i++)
		{
			hr = spSearch->GetColumn(hSearch, rgpwszAttributes[i], &col);
			if(SUCCEEDED(hr))
			{
				switch(col.dwADsType)
				{
				case ADSTYPE_DN_STRING:
					wprintf(L"%s: %s\n\n", rgpwszAttributes[i], col.pADsValues[0].DNString);
					break;

				case ADSTYPE_CASE_IGNORE_STRING:
					wprintf(L"%s: %s\n\n", rgpwszAttributes[i], col.pADsValues[0].CaseExactString);
					break;

				default:
					break;
			}
			
				// Free the column.
				spSearch->FreeColumn(&col);
		}
	}
	
		// Get the next row.
		hr = spSearch->GetNextRow(hSearch);
}

	// Close the search handle to cleanup.
	hr = spSearch->CloseSearchHandle(hSearch);

	return hr;
}

When this search is executed and the results are enumerated, it would return the name, telephone number, and office number of all of the user objects contained in the group's member attribute list.

Error handling: The results of an attribute-scope query may span multiple servers and a server may not return all the data requested for the all the rows returned. If this occurs, when the last row is retrieved by calling GetNextRow or GetFirstRow, ADSI will return S_ADS_ERRORSOCCURRED, instead of S_ADS_NOMORE_ROWS.