Directory Services

How Applications Should Use Display Specifiers

To display Microsoft® Active Directory® directory service objects, use display specifiers to obtain localized display data for class and attribute objects. This enables localized display names and icons to be used and avoids unnecessary localization of the display data.

Display Names

The classDisplayName and attributeDisplayNames properties of the display specifier objects for the appropriate locale should be used to obtain display text for class and attribute names. Do not use the cn, classDisplayName or ldapDisplayName properties of the classSchema or attributeSchema objects to obtain display text labels because these values are not localized. For more information about how to retrieve localized text for a class object, see the following sample code.

Icons

The iconPath property of the display specifier objects for the appropriate locale should be used to obtain the icon to display for a class object. For more information, see Class Icons. If no localized icon is specified for a class object, a default icon should be displayed for the item.

Creating New Objects

When possible, use the appropriate object creation wizards to create new objects. For more information, see Invoking Creation Wizards from Your Application.

The following code example demonstrates how to obtain the display text for a class and an attribute of a class.

#include <atlbase.h>

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

	GetClassDisplaySpecifierContainer()

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

HRESULT GetClassDisplaySpecifierContainer(LPWSTR pwszClass, 
										LCID locale, 
										IADs **ppads)
{
	if((NULL == pwszClass) || (NULL == ppads))
	{
		return E_INVALIDARG;
}

	*ppads = NULL;

	// If no locale is specified, use the default system locale.
	if(0 == locale)
	{
		locale = GetSystemDefaultLCID();
		if(0 == locale)
		{
			return E_FAIL;
	}
}
 
	// Be sure that it is a valid locale.
	if(!IsValidLocale(locale, LCID_SUPPORTED))
	{
		return E_INVALIDARG;
}
 
	HRESULT hr;
	IADs *padsRoot = NULL;
 
	hr = ADsOpenObject( L"LDAP://rootDSE",
						NULL,
						NULL,
						ADS_SECURE_AUTHENTICATION,
						IID_IADs,
						(void**)&padsRoot);
 
	if(SUCCEEDED(hr))
	{
		VARIANT var;

		VariantInit(&var);

		// Get the DN to the configuration container.
		hr = padsRoot->Get(CComBSTR(L"configurationNamingContext"), &var);
	
		if(SUCCEEDED(hr))
		{
			WCHAR wszPath[MAX_PATH * 2];

			// Build the string to bind to the container for the
			// specified locale in the DisplaySpecifiers container.
			wsprintfW(wszPath, 
				L"LDAP://cn=%s-Display,cn=%x,cn=DisplaySpecifiers,%s", 
				pwszClass, 
				locale, 
				var.bstrVal);

			VariantClear(&var);
		
			// Bind to the container.
			hr = ADsOpenObject( wszPath,
								NULL,
								NULL,
								ADS_SECURE_AUTHENTICATION,
								IID_IADs,
								(void**)ppads);
 
	}

		padsRoot->Release();
}

	return hr;
}

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

	GetClassDisplayLabel()

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

HRESULT GetClassDisplayLabel(LPWSTR pwszClass, 
							 LCID locale, 
							 BSTR *pbstrClassLabel)
{
	if((NULL == pwszClass) || (NULL == pbstrClassLabel))
	{
		return E_INVALIDARG;
}

	*pbstrClassLabel = NULL;

	HRESULT hr;
	IADs *padsDispSpec;
	hr = GetClassDisplaySpecifierContainer(pwszClass, locale, &padsDispSpec);
	if(SUCCEEDED(hr))
	{
		VARIANT var;

		VariantInit(&var);

		// Get the classDisplayName property value
		hr = padsDispSpec->Get(CComBSTR(L"classDisplayName"), &var);
	
		if(SUCCEEDED(hr))
		{
			if(VT_BSTR == var.vt)
			{
				// Do not free the BSTR. The caller will handle it.
				*pbstrClassLabel = var.bstrVal;
		}
			else
			{
				VariantClear(&var);
				hr = E_FAIL;
		}
	}
	
		padsDispSpec->Release();
}

	return hr;
}

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

	GetAttributeDisplayLabel()

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

HRESULT GetAttributeDisplayLabel(LPWSTR pwszClass, 
								 LPWSTR pwszAttribute, 
								 LCID locale, 
								 BSTR *pbstrAttributeLabel)
{
	if( (NULL == pwszClass) || 
		(NULL == pwszAttribute) || 
		(NULL == pbstrAttributeLabel))
	{
		return E_INVALIDARG;
}

	*pbstrAttributeLabel = NULL;

	HRESULT hr;
	IADs *padsDispSpec;
	hr = GetClassDisplaySpecifierContainer(pwszClass, locale, &padsDispSpec);
	if(SUCCEEDED(hr))
	{
		VARIANT var;

		VariantInit(&var);

		// Get the attributeDisplayNames property values
		hr = padsDispSpec->GetEx(CComBSTR(L"attributeDisplayNames"), &var);
	
		if(SUCCEEDED(hr))
		{
			LONG		lStart, 
						lEnd,
						i;
			SAFEARRAY   *psa;
			VARIANT	 varItem;

			VariantInit(&varItem);

			psa = V_ARRAY(&var);

			// Get the lower and upper bound.
			hr = SafeArrayGetLBound(psa, 1, &lStart);
			hr = SafeArrayGetUBound(psa, 1, &lEnd);

			/*
			The attributeDisplayNames values take the form 
			"<attribute name>,<display name>". Enumerate the values, looking 
			for the one that begins with the specified attribute name.
			*/
			for(i = lStart; i <= lEnd; i++)
			{
				hr = SafeArrayGetElement(psa, &i, &varItem);
				if(SUCCEEDED(hr))
				{
					WCHAR   wszTemp[MAX_PATH];

					lstrcpynW(wszTemp, 
						V_BSTR(&varItem), 
						lstrlenW(pwszAttribute) + 1);
			
					if(0 == lstrcmpiW(pwszAttribute, wszTemp))
					{
						LPWSTR  pwszDisplayLabel;

						/*
						The proper value was found. Now parse the value, looking 
						for the first comma, which delimits the attribute name 
						from the display name.
						*/
						for(	pwszDisplayLabel = V_BSTR(&varItem); 
								*pwszDisplayLabel; 
								pwszDisplayLabel = CharNextW(pwszDisplayLabel))
						{
							if(',' == *pwszDisplayLabel)
							{
								/*
								The delimiter was found. Set the string 
								pointer to the next character, which is the 
								first character of the display name.
								*/
								pwszDisplayLabel = CharNextW(pwszDisplayLabel);
								break;
						}
					}
					
						if(*pwszDisplayLabel)
						{
							// Copy the display name to the output.
							*pbstrAttributeLabel = CComBSTR(pwszDisplayLabel).Detach();

							hr = S_OK;
					}

						/*
						Release the item variant because the break prevents 
						it from getting released by the VariantClear call below.
						*/
						VariantClear(&varItem);

						break;
				}

					VariantClear(&varItem);
			}
		}
	}
	
		padsDispSpec->Release();
}

	return hr;
}