Platform SDK: Active Directory, ADSI, and Directory Services

Example Code for Creating an Attribute

The following functions create an attributeSchema object in the ADSI cache.

The CreateAttribute function creates the attributeSchema object. Note that the IADs::SetInfo must be called in order 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.

//Create an attribute with IADsContainer::Create. There is caching.
//This function creates the attributeSchema object but does not commit it.
//It is up to the caller to call IADs::SetInfo on the new attribute object
//and commit it to the ds.
HRESULT CreateAttribute(
    IADsContainer *pSchema, //IADsContainer to schema container.
    LPOLESTR szAttributeName,
    LPOLESTR szLDAPDisplayName,//
    LPOLESTR szAttributeOID,
    const GUID * pSchemaIDGUID,//
    LPOLESTR szAttributeSyntax,
    int iOmSyntax,
    PBYTE pbomObjectClass, 
    DWORD dwSizeomObjectClass, //size of pbomObjectClass.
    LPOLESTR szDescription,
    BOOL bIsSingleValued,
    BOOL bIsInGC,
    BOOL bIsIndexed,
    int iLowerRange,//
    int iUpperRange,//
    int iLinkID,//
    LPOLESTR szAdminDisplayName,//
    IADs **ppNewAttribute //Return an IADs pointer to the new attribute.
    )
{
if ((!szAttributeName)
    ||(!szAttributeOID)
    ||(!szAttributeSyntax)
    ||(!iOmSyntax)
    ||(!szAttributeSyntax)
   )
  return E_INVALIDARG;
if (!ppNewAttribute)
  return E_POINTER;
HRESULT hr = E_FAIL;
HRESULT hrPut = S_OK;
IDispatch *pDisp = NULL;
LPOLESTR szBuffer = new OLECHAR[MAX_PATH*2];
LPOLESTR szAttributeToSet = NULL;
wcscpy(szBuffer,L"cn=");
wcscat(szBuffer,szAttributeName);
(*ppNewAttribute) = NULL;
VARIANT var;
if (!pSchema)
  return E_POINTER;
hr = pSchema->Create(L"attributeSchema",szBuffer,&pDisp);
if (SUCCEEDED(hr))
{
  hr = pDisp->QueryInterface(IID_IADs, (void**)ppNewAttribute);
  while (SUCCEEDED(hr))
  {
    //Put cn Put this value so that it can be read from the cache.
    szAttributeToSet = L"cn";
    var.vt = VT_BSTR;
    var.bstrVal = SysAllocString(szAttributeName);
    hrPut = (*ppNewAttribute)->Put(szAttributeToSet,var);
    VariantClear(&var);
    hr = hrPut;
 
    //Put lDAPDisplayName.
    //If NULL, let it default (that is, don't set it).
    if (szLDAPDisplayName)
    {
      szAttributeToSet = L"lDAPDisplayName";
      var.vt = VT_BSTR;
      var.bstrVal = SysAllocString(szLDAPDisplayName);
      hrPut = (*ppNewAttribute)->Put(szAttributeToSet,var);
      VariantClear(&var);
      hr = hrPut;
    }
 
    //Put attributeID
    szAttributeToSet = L"attributeID";
    var.vt = VT_BSTR;
    var.bstrVal = SysAllocString(szAttributeOID);
    hrPut = (*ppNewAttribute)->Put(szAttributeToSet,var);
    VariantClear(&var);
    hr = hrPut;
 
    //Put schemaIDGUID.
    //If NULL, let it default (ie, don't set it).
    if (pSchemaIDGUID)
    {
      hrPut = BytesToVariantArray(
        (LPBYTE)pSchemaIDGUID, //Pointer to bytes to put in a variant array.
        sizeof(GUID),//Size of pValue in bytes.
        &var //Return variant containing octet string (VT_UI1|VT_ARRAY).
        );
      if (SUCCEEDED(hrPut))
      {
       szAttributeToSet = L"schemaIDGUID";
      hrPut = (*ppNewAttribute)->Put(szAttributeToSet,var);
      VariantClear(&var);
      }
      hr = hrPut;
    }
 
    //Put attributeSyntax
    szAttributeToSet = L"attributeSyntax";
    var.vt = VT_BSTR;
    var.bstrVal = SysAllocString(szAttributeSyntax);
    hrPut = (*ppNewAttribute)->Put(szAttributeToSet,var);
    VariantClear(&var);
    hr = hrPut;
 
    //Put oMSyntax
    szAttributeToSet = L"oMSyntax";
    var.vt = VT_I4;
    var.lVal = iOmSyntax;
    hrPut = (*ppNewAttribute)->Put(szAttributeToSet,var);
    VariantClear(&var);
    hr = hrPut;
 
    //Put searchFlags
    szAttributeToSet = L"searchFlags";
    var.vt = VT_I4;
    if (bIsIndexed)
       var.lVal = 1;
    else
       var.lVal = 0;
    hrPut = (*ppNewAttribute)->Put(szAttributeToSet,var);
    VariantClear(&var);
    hr = hrPut;
 
    //Put isSingleValued
    szAttributeToSet = L"isSingleValued";
    var.vt = VT_BOOL;
    if (bIsSingleValued)
      var.boolVal = VARIANT_TRUE;
    else
      var.boolVal = VARIANT_FALSE;
    hrPut = (*ppNewAttribute)->Put(szAttributeToSet,var);
    VariantClear(&var);
    hr = hrPut;
 
    //Put isMemberOfPartialAttributeSet
    szAttributeToSet = L"isMemberOfPartialAttributeSet";
    var.vt = VT_BOOL;
        if (bIsInGC)
      var.boolVal = VARIANT_TRUE;
    else
      var.boolVal = VARIANT_FALSE;
    hrPut = (*ppNewAttribute)->Put(szAttributeToSet,var);
    VariantClear(&var);
    hr = hrPut;
 
    //Put description
    szAttributeToSet = L"description";
    var.vt = VT_BSTR;
    var.bstrVal = SysAllocString(szDescription);
    hrPut = (*ppNewAttribute)->Put(szAttributeToSet,var);
    VariantClear(&var);
    hr = hrPut;
 
    //Put rangeLower and rangeUpper
    //If both are 0, let them default (ie, don't set them).
    if ((iLowerRange>=0)&&(iUpperRange>0))
    {
      //Make sure they are legal values
      if (iLowerRange < iUpperRange)
      {
        //Set rangeUpper
        szAttributeToSet = L"rangeUpper";
        var.vt = VT_I4;
        var.lVal = iUpperRange;
        hrPut = (*ppNewAttribute)->Put(szAttributeToSet,var);
        VariantClear(&var);
        if (SUCCEEDED(hrPut))
        {
          //Set rangeLower
          szAttributeToSet = L"rangeLower";
          var.vt = VT_I4;
          var.lVal = iLowerRange;
          hrPut = (*ppNewAttribute)->Put(szAttributeToSet,var);
          VariantClear(&var);
        }
        hr = hrPut;
      }
      else
        hr = E_INVALIDARG;
    }
 
    //Put linkID. If linkID is 0, let it default (ie, don't set it).
    if (iLinkID>0)
    {
      szAttributeToSet = L"linkID";
      var.vt = VT_I4;
      var.lVal = iLinkID;
      hrPut = (*ppNewAttribute)->Put(szAttributeToSet,var);
      VariantClear(&var);
      hr = hrPut;
    }
 
    //Put adminDisplayName.
    //If NULL, set it to the same string as cn.
 
      szAttributeToSet = L"adminDisplayName";
      var.vt = VT_BSTR;
      if (!szAdminDisplayName)
      {
        var.bstrVal = SysAllocString(szAttributeName);
      }
      else
      {
        var.bstrVal = SysAllocString(szAdminDisplayName);
      }
      hrPut = (*ppNewAttribute)->Put(szAttributeToSet,var);
      VariantClear(&var);
      hr = hrPut;
 
 
    //Put omObjectClass
    //Should only set this if it is going to be used to disambiguate omSyntax 127 attributes.
    if (pbomObjectClass && dwSizeomObjectClass) 
    {
      hrPut = BytesToVariantArray(
        pbomObjectClass, //Pointer to bytes to put in a variant array.
        dwSizeomObjectClass,//Size of pValue in bytes.
        &var //Return variant containing octet string (VT_UI1|VT_ARRAY).
        );
      if (SUCCEEDED(hrPut))
      {
       szAttributeToSet = L"omObjectClass";
      hrPut = (*ppNewAttribute)->Put(szAttributeToSet,var);
      VariantClear(&var);
      }
      hr = hrPut;
    }
    //End of properties to set.
    break;
  }
}
if (pDisp)
  pDisp->Release();
 
if (FAILED(hr))
{
  //Clean up if failed.
  if (*ppNewAttribute)
  {
    (*ppNewAttribute)->Release();
    (*ppNewAttribute) = NULL;
  }
    
}
 
return hr;
}
 
HRESULT BytesToVariantArray(
    PBYTE pValue, //Pointer to bytes to put in a variant array.
    ULONG cValueElements,//Size of pValue in bytes.
    VARIANT *pVariant //Return variant containing octet string (VT_UI1|VT_ARRAY).
    )
{
    HRESULT hr = E_FAIL;
    SAFEARRAY *pArrayVal = NULL;
    SAFEARRAYBOUND arrayBound;
    CHAR HUGEP *pArray = NULL;
 
    //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 (!(pArrayVal == NULL) )
    {
        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
        {
            //Clean up if array can't be accessed.
            if ( pArrayVal )
               SafeArrayDestroy( pArrayVal );
        }
    }
    else
    {
      hr = E_OUTOFMEMORY;
    }
 
    return hr;
}