Directory Services

Example Code for Creating an Attribute

The following code example creates an attributeSchema object in the schema container.

The CreateAttribute function creates an attributeSchema object in the schema container, but does not commit it to the directory. Call the IADs.SetInfo method to commit the new attributeSchema object to the directory.

The BytesToVariantArray function is a utility function that packs an octet string into a variant array.

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

	BytesToVariantArray()

	Converts an arrya of BYTEs into a VARIANT array.

	Parameters:

	pValue = Contains an array of BYTES to convert. cValueElements contains 
	the number of elements in this array.

	cValueElements - Contains the number of elements in the pValue array.

	pVariant - Receives the VARIANT that contains an octet string (VT_UI1 | 
	VT_ARRAY)

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

HRESULT BytesToVariantArray(
	PBYTE pValue,
	ULONG cValueElements,
	VARIANT *pVariant
	)
{
	HRESULT hr = E_FAIL;
	SAFEARRAY *pArrayVal = NULL;
	SAFEARRAYBOUND arrayBound;
	CHAR HUGEP *pArray = NULL;

	// Check parameters.
	if((NULL == pValue) || !(cValueElements > 0))
	{
		return E_INVALIDARG;
}
 
	// Set bound for array.
	arrayBound.lLbound = 0;
	arrayBound.cElements = cValueElements;
 
	// Create the safe array for the octet string. unsigned char elements;single dimension;aBound size.
	pArrayVal = SafeArrayCreate(VT_UI1, 1, &arrayBound);
 
	if (!(NULL == pArrayVal))
	{
		hr = SafeArrayAccessData(pArrayVal, (void HUGEP * FAR *) &pArray);
		if (SUCCEEDED(hr))
		{
			// Copy the bytes to the safe array.
			memcpy(pArray, pValue, arrayBound.cElements);
			SafeArrayUnaccessData(pArrayVal);
		
			// Set type to array of unsigned char.
			V_VT(pVariant) = VT_ARRAY | VT_UI1;
		
			// Assign the safe array to the array member.
			V_ARRAY(pVariant) = pArrayVal;
		
			hr = S_OK;
	}
		else
		{
			// Cleanup if the array cannot be accessed.
			if (pArrayVal)
			{
				SafeArrayDestroy(pArrayVal);
		}
	}
}
	else
	{
	hr = E_OUTOFMEMORY;
}
 
	return hr;
}

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

	CreateAttribute()

	Creates a new attribute in the schema container IADsContainer.Create. 
	This function creates the attributeSchema object, but does not commit it 
	to the directory. The caller must call IADs.SetInfo on the new attribute 
	object to commit it to the directory.

	Parameters:

	pwszAttributeName - Contains a null-terminated string that contains the 
	name of the attribute.

	pwszLDAPDisplayName - Contains a null-terminated string that contains 
	the lDAPDisplayName of the attribute.

	pwszAttributeOID - Contains a null-terminated string that contains the 
	OID of the attribute.

	pSchemaIDGUID - 
	pwszAttributeSyntax - Contains a null-terminated string that contains the 
	syntax of the attribute.

	iOmSyntax - Contains the value for the oMSyntax attribute.

	pbomObjectClass - Pointer to a BYTE array that contains the value for the 
	oMObjectClass attribute. dwSizeomObjectClass contains the number of elements 
	in this array.

	dwSizeomObjectClass - Contains the number of elements in the 
	pbomObjectClass array.

	pwszDescription - Contains a null-terminated string that contains the 
	description of the attribute.

	fIsSingleValued - Contains TRUE if the attribute is single-valued or FALSE 
	if the attribute is multi-valued.

	fIsInGC - Contains TRUE if the attribute is contained in the global catalog 
	or FALSE if the attribute should not be contained in the global catalog.

	fIsIndexed - Contains TRUE if the attribute is indexed or FALSE if the 
	attribute is not indexed.

	iLowerRange - Contains the lower range of the attribute value. Contains 
	zero if the range should not be set.

	iUpperRange - Contains the upper range of the attribute value. Contains 
	zero if the range should not be set.

	iLinkID - Contains the value for the linkID attribute. If this cotnains 
	zero, the linkID attribute is not set.

	pwszAdminDisplayName - Contains a null-terminated string that contains 
	the admin display name of the attribute.

	ppadsNewAttribute - Receives a pointer to the IADs interface of the 
	attribute object created.

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

HRESULT CreateAttribute(
	LPCWSTR pwszAttributeName,
	LPCWSTR pwszLDAPDisplayName,
	LPCWSTR pwszAttributeOID,
	const GUID * pSchemaIDGUID,
	LPCWSTR pwszAttributeSyntax,
	int iOmSyntax,
	PBYTE pbomObjectClass, 
	DWORD dwSizeomObjectClass,
	LPCWSTR pwszDescription,
	BOOL fIsSingleValued,
	BOOL fIsInGC,
	BOOL fIsIndexed,
	int iLowerRange,
	int iUpperRange,
	int iLinkID,
	LPCWSTR pwszAdminDisplayName,
	IADs **ppadsNewAttribute
	)
{
	if(!ppadsNewAttribute)
	{
		return E_POINTER;
}

	*ppadsNewAttribute = NULL;

	if( (!pwszAttributeName) ||
		(!pwszAttributeOID) ||
		(!pwszAttributeSyntax) ||
		(!iOmSyntax)
	)
	{
		return E_INVALIDARG;
}

	if(iLowerRange < iUpperRange)
	{
		return E_INVALIDARG;
}

	HRESULT hr;
	CComPtr<IADs> spRoot;

	hr = ADsGetObject(L"LDAP://RootDSE",
					IID_IADs,
					(void**)&spRoot);
	if(FAILED(hr))
	{
		return hr;
}

	CComVariant svarSchema;
	 
	// Get the disintguished name of the schema container.
	hr = spRoot->Get(CComBSTR("schemaNamingContext"), &svarSchema);
	if(FAILED(hr))
	{
		return hr;
}

	// Bind to the IADsContainer interface of the schema container.
	CComPtr<IADsContainer> spSchemaCont;
	hr = ADsOpenObject( L"LDAP://RootDSE",
						NULL,
						NULL,
						ADS_SECURE_AUTHENTICATION,
						IID_IADs,
						(void**)&spSchemaCont);
	if(FAILED(hr))
	{
		return hr;
}

	CComPtr<IDispatch> spDisp;

	CComBSTR sbstrAttribute = "CN=";
	sbstrAttribute += pwszAttributeName;

	// Create the object in the schema container.
	hr = spSchemaCont->Create(CComBSTR("attributeSchema"), sbstrAttribute, &spDisp);
	CComPtr<IADs> spNewAttribute;
	hr = spDisp->QueryInterface(IID_IADs, (void**)&spNewAttribute);
	if(FAILED(hr))
	{
		return hr;
}

	CComBSTR sbstrAttributeToSet;
	CComVariant svar;

	// Put this value so that it can be read from the cache.
	sbstrAttributeToSet = "cn";
	svar = pwszAttributeName;
	hr = spNewAttribute->Put(sbstrAttributeToSet, svar);
	if(FAILED(hr))
	{
		return hr;
}

	// Put lDAPDisplayName.
	// If NULL, let it default; that is, do not set it.
	if (pwszLDAPDisplayName)
	{
		sbstrAttributeToSet = "lDAPDisplayName";
		svar = pwszLDAPDisplayName;
		hr = spNewAttribute->Put(sbstrAttributeToSet, svar);
		if(FAILED(hr))
		{
			return hr;
	}
}

	// Put attributeID.
	sbstrAttributeToSet = "attributeID";
	svar = pwszAttributeOID;
	hr = spNewAttribute->Put(sbstrAttributeToSet, svar);
	if(FAILED(hr))
	{
		return hr;
}


	// Put schemaIDGUID.
	// If NULL, let it default; that is, do not set it.
	if (pSchemaIDGUID)
	{
		hr = BytesToVariantArray(
			(LPBYTE)pSchemaIDGUID,
			sizeof(GUID),
			&svar
			);
		if(FAILED(hr))
		{
			return hr;
	}

		sbstrAttributeToSet = "schemaIDGUID";
		hr = spNewAttribute->Put(sbstrAttributeToSet, svar);
		if(FAILED(hr))
		{
			return hr;
	}
}

	// Put attributeSyntax.
	sbstrAttributeToSet = "attributeSyntax";
	svar = pwszAttributeSyntax;
	hr = spNewAttribute->Put(sbstrAttributeToSet, svar);
	if(FAILED(hr))
	{
		return hr;
}

	// Put oMSyntax.
	sbstrAttributeToSet = "oMSyntax";
	svar = iOmSyntax;
	hr = spNewAttribute->Put(sbstrAttributeToSet, svar);
	if(FAILED(hr))
	{
		return hr;
}

	// Put searchFlags.
	sbstrAttributeToSet = "searchFlags";
	if (fIsIndexed)
	{
		svar = 1;
}
	else
	{
		svar = 0;
}
	hr = spNewAttribute->Put(sbstrAttributeToSet, svar);
	if(FAILED(hr))
	{
		return hr;
}

	// Put isSingleValued.
	sbstrAttributeToSet = "isSingleValued";
	if (fIsSingleValued)
	{
		svar = VARIANT_TRUE;
}
	else
	{
		svar = VARIANT_FALSE;
}
	hr = spNewAttribute->Put(sbstrAttributeToSet, svar);
	if(FAILED(hr))
	{
		return hr;
}


	// Put isMemberOfPartialAttributeSet.
	sbstrAttributeToSet = "isMemberOfPartialAttributeSet";
	if (fIsInGC)
	{
		svar = VARIANT_TRUE;
}
	else
	{
		svar = VARIANT_FALSE;
}
	svar.vt = VT_BOOL;
	hr = spNewAttribute->Put(sbstrAttributeToSet, svar);
	if(FAILED(hr))
	{
		return hr;
}


	// Put description.
	sbstrAttributeToSet = "description";
	svar = pwszDescription;
	hr = spNewAttribute->Put(sbstrAttributeToSet, svar);
	if(FAILED(hr))
	{
		return hr;
}

	// Put rangeLower and rangeUpper.
	// If both are 0, let them default; that is do not set them.
	if ((iLowerRange >= 0) && (iUpperRange > 0))
	{
		// Set rangeUpper.
		sbstrAttributeToSet = "rangeUpper";
		svar = iUpperRange;
		hr = spNewAttribute->Put(sbstrAttributeToSet, svar);
		if(FAILED(hr))
		{
			return hr;
	}

		// Set rangeLower.
		sbstrAttributeToSet = "rangeLower";
		svar = iLowerRange;
		hr = spNewAttribute->Put(sbstrAttributeToSet, svar);
		if(FAILED(hr))
		{
			return hr;
	}
}

	// Put linkID. If linkID is 0, let it default; that is, do not set it.
	if (iLinkID>0)
	{
		sbstrAttributeToSet = "linkID";
		svar = iLinkID;
		hr = spNewAttribute->Put(sbstrAttributeToSet, svar);
		if(FAILED(hr))
		{
			return hr;
	}
}

	// Put adminDisplayName.
	// If NULL, set it to the same string as cn.
	sbstrAttributeToSet = "adminDisplayName";
	if (!pwszAdminDisplayName)
	{
		svar = pwszAttributeName;
}
	else
	{
		svar = pwszAdminDisplayName;
}
	hr = spNewAttribute->Put(sbstrAttributeToSet, svar);
	if(FAILED(hr))
	{
		return hr;
}
	
	// Put omObjectClass.
	// Only set this if used to delineate omSyntax 127 attributes.
	if (pbomObjectClass && dwSizeomObjectClass) 
	{
		hr = BytesToVariantArray(
			pbomObjectClass,
			dwSizeomObjectClass,
			&svar
			);
		if(FAILED(hr))
		{
			return hr;
	}

		sbstrAttributeToSet = "omObjectClass";
		hr = spNewAttribute->Put(sbstrAttributeToSet, svar);
		if(FAILED(hr))
		{
			return hr;
	}
}

	// Get a copy of the interface to return.
	hr = spNewAttribute->QueryInterface(IID_IADs, (LPVOID*)ppadsNewAttribute);
	 
	return hr;
}