Directory Services

Reading attributeSchema and classSchema Objects

This topic provides code examples and guidelines for reading directly from attributeSchema and classSchema objects in the schema container. Be aware that, in most programming situations, when you must read data about a class or attribute definition, it is more effective to read data from the abstract schema as described in Reading the Abstract Schema.

The interfaces and techniques used to read from the schema container are those used to read any object in Active Directory. The guidelines include:

For a code example that demonstrates different methods of searching for schema objects, see Example Code for Searching for Schema Objects.

The following code example binds to an IADsContainer pointer on the schema container and then uses the ADsBuildEnumerator and ADsEnumerateNext functions to enumerate its contents. Be aware that the enumeration includes all attributeSchema and classSchema objects as well as a single subSchema object, which is the abstract schema.

For each enumerated object, the code example uses the IADs.Class property to determine whether it is an attributeSchema or classSchema object. The code example shows how to read the properties that are unavailable from the abstract schema.

#include <windows.h>
#include <stdio.h>
#include <ole2.h>
#include <activeds.h>
#include <atlbase.h>

// Forward declarations.
void ProcessAttribute(IADs *pChild);
void ProcessClass(IADs *pChild);

// ****************************************************************
//  wmain
// ****************************************************************
int wmain(int argc, WCHAR* argv[])
{
	HRESULT hr;

	hr = CoInitialize(NULL);
	if(SUCCEEDED(hr))
	{
		CComBSTR sbstrDSPath;
		CComVariant svar;
		IADs *pRootDSE = NULL;
		IADsContainer *pSchema = NULL;  
		IEnumVARIANT *pEnum = NULL;
		ULONG lFetch;
		CComBSTR sbstrClass; 
		DWORD dwClasses = 0, dwAttributes = 0, dwUnknownClass = 0;

		// Bind to rootDSE to get the schemaNamingContext property.
		hr = ADsGetObject(L"LDAP://rootDSE", IID_IADs, (void**)&pRootDSE);
		if (FAILED(hr)) 
		{
			wprintf(L"ADsGetObject failed: 0x%x\n", hr);
			goto cleanup;
	}

		hr = pRootDSE->Get(CComBSTR("schemaNamingContext"), &svar);
		sbstrDSPath = "LDAP://";
		sbstrDSPath += svar.bstrVal;

		// Bind to the actual schema container.
		wprintf(L"Binding to %s\n", sbstrDSPath);
		hr = ADsGetObject(sbstrDSPath, IID_IADsContainer, (void**) &pSchema);
		if (FAILED(hr)) 
		{
			wprintf(L"ADsGetObject to schema failed: 0x%x\n", hr);
			goto cleanup;
	}

		// Enumerate the attribute and class objects in the schema container.
		hr = ADsBuildEnumerator(pSchema, &pEnum);
		if (FAILED(hr)) 
		{
			wprintf(L"ADsBuildEnumerator failed: 0x%x\n", hr);
			goto cleanup;
	}

		hr = ADsEnumerateNext(pEnum, 1, &svar, &lFetch);
		while(S_OK == hr && 1 == lFetch)
		{
			IADs *pChild = NULL;

			// Get an IADs pointer on the child object.
			hr = V_DISPATCH(&svar)->QueryInterface(IID_IADs, (void**) &pChild);
			if (SUCCEEDED(hr)) 
			{
				// Verify that this is a class, attribute, or subSchema object.
				hr = pChild->get_Class(&sbstrClass);
				if (SUCCEEDED(hr)) 
				{
					// Get data. This depends on type of schema element.
					if (_wcsicmp(L"classSchema", sbstrClass) == 0)
					{
						dwClasses++;
						wprintf(L"%s\n", sbstrClass);
						ProcessClass(pChild);
						wprintf(L"\n");
				}
					else if (_wcsicmp(L"attributeSchema", sbstrClass) == 0)
					{
						dwAttributes++;
						wprintf(L"%s\n", sbstrClass);
						ProcessAttribute(pChild);
						wprintf(L"\n");
				}
					else if (_wcsicmp(L"subSchema", sbstrClass) == 0) 
					{
						wprintf(L"abstract schema");
						wprintf(L"\n");
				}
					else
					{
						dwUnknownClass++;
				}
			}

				pChild->Release();
		}
		
			hr = ADsEnumerateNext(pEnum, 1, &svar, &lFetch);
	}
	
		wprintf(L"Classes: %u\nAttributes: %u\nUnknown: %u\n", dwClasses, dwAttributes, dwUnknownClass);

cleanup:
		if (pRootDSE)
		{
			pRootDSE->Release();
	}

		if (pEnum)
		{
			ADsFreeEnumerator(pEnum);
	}

		if (pSchema)
		{
			pSchema->Release();
	}

}

	CoUninitialize();

	return 0;
}

// ****************************************************************
//  PrintGUIDFromVariant
//  Prints a GUID in string format.
// ****************************************************************
void PrintGUIDFromVariant(VARIANT varGUID)
{
	HRESULT hr;
	void HUGEP *pArray;
	WCHAR szGUID[40];
	LPGUID pGUID;

	DWORD dwLen = sizeof(GUID);

	hr = SafeArrayAccessData(V_ARRAY(&varGUID), &pArray);
	if(SUCCEEDED(hr))
	{
		pGUID = (LPGUID)pArray;

		// Convert GUID to string.
		StringFromGUID2(*pGUID, szGUID, 40); 
									
		// Print GUID.
		wprintf(L",%s",szGUID);

		SafeArrayUnaccessData(V_ARRAY(&varGUID));
}
}

// ****************************************************************
// PrintPropertyValue
// ****************************************************************
void PrintPropertyValue(VARIANT var, BSTR bstrPropName, HRESULT hr) 
{
	if (E_ADS_PROPERTY_NOT_FOUND == hr)
	{
		wprintf(L"-- not set --\n");
}
	else if (FAILED(hr))
	{
		wprintf(L"get %s failed: 0x%x\n", bstrPropName, hr);
}
	else 
	{
		switch (var.vt) 
		{
			case VT_BSTR:
				wprintf(L"%s\n", var.bstrVal);
				break;

			case VT_I4:
				wprintf(L"%u\n", var.iVal);
				break;

			case VT_BOOL:
				wprintf(L"%s\n", var.boolVal ? L"TRUE" : L"FALSE");
				break;

			default:
				wprintf(L"-- ??? --\n");
	}
}
}

// ****************************************************************
//  ProcessAttribute
//  Gets information about an attributeClass object.
// ****************************************************************
void ProcessAttribute(IADs *pChild)
{
	HRESULT hr;
	CComVariant svar;
	CComBSTR sbstrPropName;

	// Get the attribute Common-Name (cn) property. 
	sbstrPropName = "cn";
	hr = pChild->Get(sbstrPropName, &svar);
	PrintPropertyValue(svar, sbstrPropName, hr);

	// Get the attribute lDAPDisplayName.
	sbstrPropName = "lDAPDisplayName";
	hr = pChild->Get(sbstrPropName, &svar);
	PrintPropertyValue(svar, sbstrPropName, hr);

	// Get the class linkID. 
	sbstrPropName = "linkID";
	hr = pChild->Get(sbstrPropName, &svar);
	PrintPropertyValue(svar, sbstrPropName, hr);

	// Get the attribute schemaIDGUID. 
	sbstrPropName = "schemaIDGUID";
	hr = pChild->Get(sbstrPropName, &svar);
	if (E_ADS_PROPERTY_NOT_FOUND == hr)
	{
		wprintf(L"-- not set --\n");
}
	else if(SUCCEEDED(hr))
	{
		PrintGUIDFromVariant(svar);
}

	// Get the attribute attributeSecurityGUID. 
	sbstrPropName = "attributeSecurityGUID";
	hr = pChild->Get(sbstrPropName, &svar);
	if (E_ADS_PROPERTY_NOT_FOUND == hr)
	{
		wprintf(L"-- not set --\n");
}
	else if (SUCCEEDED(hr))
	{
		PrintGUIDFromVariant(svar);
}

	// Get the attribute attributeSyntax property. 
	sbstrPropName = "attributeSyntax";
	hr = pChild->Get(sbstrPropName, &svar);
	PrintPropertyValue(svar, sbstrPropName, hr);

	// Get the attribute's oMSyntax property. 
	sbstrPropName = "oMSyntax";
	hr = pChild->Get(sbstrPropName, &svar);
	PrintPropertyValue(svar, sbstrPropName, hr);
}

// ****************************************************************
// ProcessClass
// Gets information about a schema class.
// ****************************************************************
void ProcessClass(IADs *pChild)
{
	HRESULT hr;
	CComVariant svar;
	CComBSTR sbstrPropName;

	// Get the class cn.
	sbstrPropName = "cn";
	hr = pChild->Get(sbstrPropName, &svar);
	PrintPropertyValue(svar, sbstrPropName, hr);

	// Get the class lDAPDisplayName.
	sbstrPropName = "lDAPDisplayName";
	hr = pChild->Get(sbstrPropName, &svar);
	PrintPropertyValue(svar, sbstrPropName, hr);

	// Get the class schemaIDGUID. 
	sbstrPropName = "schemaIDGUID";
	hr = pChild->Get(sbstrPropName, &svar);
	if (FAILED(hr))
	{
		wprintf(L",get schemaIDGUID failed: 0x%x", hr);
}
	else 
	{
		PrintGUIDFromVariant(svar);
}

	// Get the class adminDescription property. 
	sbstrPropName = "adminDescription";
	hr = pChild->Get(sbstrPropName, &svar);
	PrintPropertyValue(svar, sbstrPropName, hr);

	// Get the class adminDisplayName property. 
	sbstrPropName = "adminDisplayName";
	hr = pChild->Get(sbstrPropName, &svar);
	PrintPropertyValue(svar, sbstrPropName, hr);

	// Get the class rDNAttID. 
	sbstrPropName = "rDNAttID";
	hr = pChild->Get(sbstrPropName, &svar);
	PrintPropertyValue(svar, sbstrPropName, hr);

	// Get the class defaultHidingValue. 
	sbstrPropName = "defaultHidingValue";
	hr = pChild->Get(sbstrPropName, &svar);
	PrintPropertyValue(svar, sbstrPropName, hr);

	// Get the class defaultObjectCategory. 
	sbstrPropName = "defaultObjectCategory";
	hr = pChild->Get(sbstrPropName, &svar);
	PrintPropertyValue(svar, sbstrPropName, hr);

	// Get the class systemOnly value.
	sbstrPropName = "systemOnly";
	hr = pChild->Get(sbstrPropName, &svar);
	PrintPropertyValue(svar, sbstrPropName, hr);

	// Get the class defaultSecurityDescriptor.
	sbstrPropName = "defaultSecurityDescriptor";
	hr = pChild->Get(sbstrPropName, &svar);
	PrintPropertyValue(svar, sbstrPropName, hr);
}