Active Directory and ADSI (ldap in fact) have many unutilized features that, if the APIs would explain better be used much better!

Let me draw a situation for you.

Imagine you have an app, that is distributed and sold among international customers (congrats for that by the way J). Now there could happen 2 things, or 3 if nothing happens is a count as well).

1)      Your company suddenly got sold out to a rich guy or girl and now everything gets renamed including your ‘custom company container’.
So cn=ourcompany data,cn=Program Data,dn=nwtraders,dc=msft
Must become (because your boss asks you to do so!)
cn=thenewcompany data,cn=Program data,dn=nwtraders,dc=msft

2)      Some dn (distinguished Name) is subject to translation. Eg:
cn=Program Information,cn=etc blah blah
must be translated (because your boss asks you to do so
J ) to
cn=Information du programme,cn=etc blah blah

In both situations 1 and 2 you’ll have to keep a list of paths per language and many companies of course, understand this is a difficult and risky task, so they might choose to keep up with a single international English name.

Note: The WKGUID protects against renaming or translating, but it is not meant to ‘protect’ against moving your container within Active Directory)

Why should you accept this risk if Active Directory supports immunity against renaming this out of the box if you associate a WKGUID?

However there is no obvious documentation about this and you just should know that setting this property more or less is hidden through usage of IDirectoryObject while ADSI /scripting/vb6 environments were not able to use this interface. 

Worse, the sample at the MSDN might be working but goes lowest by using the ldap_connect API within a ADSI call (that’s rather dirty) instead of using CLSID_DNWithBinary and the sample is flawed with leaks but let’s not bash the writer, I’m not intending to do so).

(update) b.t.w. in the attachment temp.zip you'll find full source code for the sample. Note that Active Directory has a bug that makes custom WKGUIDs useless. It simply cannot find your custom WKGUID. I reported this bug to Microsoft.

How could you ever figure that WKGUID and it’s query syntax is related to the following attribute: ‘otherWellKnownObjects’?J.

This sort of documentation, it's complexity in Active Directory on LDAP makes it hard to see any utilization for this feature.

I hope to support MS-es Active Directory (that I love, just as IIS and SQL server) usage and ease in practice by posting the following sample code.

Note that this code is not ‘well-formed’ and ‘error-proof’ but it works and gives you an idea.

The logging (debug feature) is done through the logging module posted just before this posting.

Now how do we profit from this feature?

Imagine you have created through an LDIF or through code a container object

cn=our CompanyData,cn=Program Data,dc=nwtraders,dc=msft

1)      Now we associate ‘our CompanyData’ with the ‘otherWellKnownObjects’ property using a custom hexadecimal guid  81a3469a653959419309b33e27bdd2c9”!

2)      Eg: WriteADSAttributeDnWithString([ourcompnayCN], L”cn=our Company Data,cn=Program Data,dc=nwtraders,dc=msft”, ourGUID, ADS_ATTR_APPEND

3)      Now you can query your company data like this, no matter your company is being renamed in futureJ, through the following syntax: LDAP://<WKGUID=8a3469a653959419309b33e27bdd2c9,cn=Program Data,dc=nwtraders,dc=msft>

Normally the dn for Program Data is WKGUID-ed by Microsoft as well! The Program Data container should be reliably formatted as L"LDAP://<WKGUID=%s,%s>", GUID_PROGRAM_DATA_CONTAINER_W, L”dc=nwtraders,dc=msft”)

Note: After you usage of the WKGUID or the GUID- syntax, you must resolve the fully qualified dn by using the ‘distinguishedName’ property and refetching your Actife Directory object before you go on with it.

 


Note: You would wish you could set the objectGUID attribute at creation of your object instead of the WKGUID. Unfortunately, this value is generated by Active Directory itself.)

STDMETHODIMP WriteADSAttributeDnWithString(

IADs *piADs,

PCWSTR dn,

PCWSTR guid)

{

      CComVariant v;

     CComPtr<IADsDNWithBinary> dnWithBin;

      CComBSTR assocDn(dn);

     dnWithBin.CoCreateInstance(CLSID_DNWithBinary);

      v.vt = VT_UI1 | VT_ARRAY;

hr = ConvertHexGuidToArray(guid, &v.parray);

      hr = dnWithBin->put_BinaryValue(v);

      hr = dnWithBin->put_DNString(assocDn);

CComBSTR attribute( L"otherWellKnownObjects");

     

v.Clear();

      v.vt = VT_DISPATCH;          

      dnWithBin.QueryInterface(&v.pdispVal);

      hr = pADs->Put(attribute, v);

      hr = pADs->SetInfo();

     

      return hr;

}

// converts hexadecimal string to a octet encoded byte array

STDMETHODIMP ConvertHexGuidToArray(PCWSTR hexGuid, LPSAFEARRAY *sa)

{

      if (hexGuid == NULL) return E_INVALIDARG;

      int hexLen = (int)wcslen(hexGuid);

      HRESULT hr = S_OK;

      CTempBuffer<CHAR> sHex(hexLen);

      int loopLen = hexLen;

      while(loopLen-- != 0)  

            sHex[loopLen] = (CHAR) hexGuid[loopLen];

      SAFEARRAY *temp= SafeArrayCreateVector(VT_UI1, 0, hexLen / 2);

      if (temp == NULL)

            return E_OUTOFMEMORY;

      PBYTE dst ;

      hr = SafeArrayAccessData(temp, (void**)&dst);

      int dstLen = hexLen /2;

      if (hr == S_OK)

      {

            AtlHexDecode(sHex, hexLen, dst, &dstLen );

            hr = SafeArrayUnaccessData(temp);

            *sa = temp; //return the array

      }

      return hr;

}