| Platform SDK: Active Directory, ADSI, and Directory Services |
The following program creates two objects: a container object and a sub-container object within it. A value for the sub-container object is added to the otherWellKnownObjects property of the container object. The program binds to the sub-container object using the WKGUID binding and displays its ADsPath. It then renames the sub-container object and binds again using the same WKGUID binding.
#include <wchar.h>
#include <objbase.h>
#include <activeds.h>
//Make sure you define UNICODE
//Need to define version 5 for Windows 2000
#define _WIN32_WINNT 0x0500
//FOR LDAP API...Required for Beta 3 only.
#include <winldap.h>
//Need to link against the following LIBs:
//wldap32.lib
static GUID MyWKOTestObjectGUID = { /* df447b5e-aa5b-11d2-8d53-00c04f79ab81 */
0xdf447b5e,
0xaa5b,
0x11d2,
{0x8d, 0x53, 0x00, 0xc0, 0x4f, 0x79, 0xab, 0x81}
};
HRESULT GUIDtoBindableString (LPGUID pGUID, LPOLESTR *ppGUIDString);
HRESULT AddValueToOtherWKOProperty(LPOLESTR szContainerDN, //DN for container whose otherWellKnownObjects property to modify
LPGUID pWKOGUID, //WKO GUID for the well-known object.
LPOLESTR szWKOObjectDN //DN of the well-known object.
);
void wmain( int argc, wchar_t *argv[ ])
{
LPOLESTR pszBuffer = new OLECHAR[MAX_PATH*2];
wprintf(L"This program does the following:\n");
wprintf(L"1. Creates a container (MyWKOTestContainer) in the current Window 2000 domain.\n");
wprintf(L"2. Creates a container object (MyWKOTestObject) within the container.\n");
wprintf(L"3. Adds a value for the container object on the otherWellKnownObject property of the container.\n");
wprintf(L"4. Binds to the container object using WKGUID binding string.\n");
wprintf(L"5. Renames the container object using WKGUID binding string.\n");
wprintf(L"6. Binds to the container object using WKGUID binding string.\n");
wprintf(L"7. Optionally, cleans up by removing the container and container object.\n\n");
//Intialize COM
CoInitialize(NULL);
HRESULT hr = S_OK;
IADs *pObject = NULL;
IADsContainer *pDomain = NULL;
IDispatch *pDisp = NULL;
IDispatch *pDispNewObject = NULL;
IADsContainer *pNewContainer = NULL;
IADs *pIADsObject = NULL;
IADs *pNewObject = NULL;
IADs *pTestWKO1 = NULL;
IADs *pTestWKO2 = NULL;
VARIANT vartest;
BSTR bstr;
LPOLESTR szNewContainerDN = new OLECHAR[MAX_PATH];
LPOLESTR szPath = new OLECHAR[MAX_PATH];
LPOLESTR szRelPath = new OLECHAR[MAX_PATH];
LPOLESTR szGUIDString = NULL;
//Names of the container and child object.
LPOLESTR szContainer = L"MyWKOTestContainer";
LPOLESTR szNewObject = L"MyWKOTestObject";
LPOLESTR szNewObjectRenameRDN = L"cn=ObjectwithNEWNAME";
//Get rootDSE and the domain container's DN.
VARIANT var;
hr = ADsOpenObject(L"LDAP://rootDSE",
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
IID_IADs,
(void**)&pObject);
if (FAILED(hr))
{
wprintf(L"Not Found. Could not bind to the domain.\n");
if (pObject)
pObject->Release();
return;
}
hr = pObject->Get(L"defaultNamingContext",&var);
if (SUCCEEDED(hr))
{
//Build the ADsPath to the domain
wcscpy(szPath,L"LDAP://");
wcscat(szPath,var.bstrVal);
VariantClear(&var);
//Bind to the current domain.
hr = ADsOpenObject(szPath,
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
IID_IADsContainer,
(void**)&pDomain);
if (SUCCEEDED(hr))
{
//Create the container.
wcscpy(szRelPath,L"cn=");
wcscat(szRelPath,szContainer);
hr = pDomain->Create(L"container", //ldapDisplayName of the class of the object to create.
szRelPath, //relative path in RDN=value format
&pDisp); //return an IDispatch pointer to the new object.
if (SUCCEEDED(hr))
{
//QI for an IADs interface.
hr = pDisp->QueryInterface(IID_IADs, (void **)&pIADsObject);
//Commit the new object to the directory.
hr = pIADsObject->SetInfo();
//QI for an IADsContainer interface.
hr = pDisp->QueryInterface(IID_IADsContainer, (void **)&pNewContainer);
if (SUCCEEDED(hr))
{
//Create the new container object in the container.
wcscpy(szRelPath,L"cn=");
wcscat(szRelPath,szNewObject);
hr = pNewContainer->Create(L"container", //ldapDisplayName of the class of the object to create.
szRelPath, //relative path in RDN=value format
&pDispNewObject); //return an IDispatch pointer to the new object.
if (SUCCEEDED(hr))
{
//Get the DN of the new container object
hr = pIADsObject->Get(L"distinguishedName", &var);
if (SUCCEEDED(hr))
{
wcscpy(szNewContainerDN, var.bstrVal);
VariantClear(&var);
wprintf(L"Created new container with DN: %s\n",szNewContainerDN);
hr = pDispNewObject->QueryInterface(IID_IADs, (void **)&pNewObject);
if (SUCCEEDED(hr))
{
//Commit the new object to the directory.
hr = pNewObject->SetInfo();
//Get the DN for the new object
hr = pNewObject->Get(L"distinguishedName", &var);
if (SUCCEEDED(hr))
{
wprintf(L"Created new child object with DN: %s\n",var.bstrVal);
//FOR BETA 3 only. Need to use LDAP API to set the otherWellKnownObjects property.
wprintf(L"Call AddValueToOtherWKOProperty with:\n");
wprintf(L"szContainer DN: %s\n",szNewContainerDN);
GUIDtoBindableString (&MyWKOTestObjectGUID, &szGUIDString);
wprintf(L"pWKOGUID (bindable string format): %s\n",szGUIDString);
wprintf(L"szWKOObjectDN: %s\n",var.bstrVal);
hr = AddValueToOtherWKOProperty(szNewContainerDN, //DN for container whose otherWellKnownObjects property to modify
&MyWKOTestObjectGUID, //WKO GUID for the well-known object.
var.bstrVal //DN of the well-known object.
);
wprintf(L"AddValueToOtherWKOProperty returned: %x\n",hr);
if (SUCCEEDED(hr))
{
//Now bind using WKGUID binding
//Build the ADsPath to the well-known object
wcscpy(szPath,L"LDAP://<WKGUID=");
wcscat(szPath,szGUIDString);
wcscat(szPath,L",");
wcscat(szPath,szNewContainerDN);
wcscat(szPath,L">");
wprintf(L"Bind with the following WKGUID binding string: %s\n",szPath);
hr = ADsOpenObject(szPath,
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
IID_IADs,
(void**)&pTestWKO1);
if (SUCCEEDED(hr))
{
hr = pTestWKO1->Get(L"distinguishedName",&vartest);
if (SUCCEEDED(hr))
{
wprintf(L"Successfully bound to object. DN: %s\n",vartest.bstrVal);
VariantClear(&vartest);
}
}
else
wprintf(L"Binding failed with hr: %x\n",hr);
if (pTestWKO1)
pTestWKO1->Release();
//Bind again using the DN to get a regular ADsPath.
wcscpy(szPath,L"LDAP://");
wcscat(szPath,var.bstrVal);
hr = ADsOpenObject(szPath,
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
IID_IADs,
(void**)&pTestWKO1);
hr = pTestWKO1->get_ADsPath(&bstr);
//Rename the WKO object
hr = pNewContainer->MoveHere(bstr,szNewObjectRenameRDN,NULL);
FreeADsStr(bstr);
if (pTestWKO1)
pTestWKO1->Release();
//Now AGAIN bind using WKGUID binding
//Build the ADsPath to the well-known object
wcscpy(szPath,L"LDAP://<WKGUID=");
wcscat(szPath,szGUIDString);
wcscat(szPath,L",");
wcscat(szPath,szNewContainerDN);
wcscat(szPath,L">");
wprintf(L"Bind AGAIN with the following WKGUID binding string: %s\n",szPath);
hr = ADsOpenObject(szPath,
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
IID_IADs,
(void**)&pTestWKO2);
if (SUCCEEDED(hr))
{
hr = pTestWKO2->Get(L"distinguishedName",&vartest);
if (SUCCEEDED(hr))
{
wprintf(L"Successfully bound to object (Note the DN reflects the rename). DN: %s\n",vartest.bstrVal);
VariantClear(&vartest);
}
}
else
wprintf(L"Binding failed with hr: %x\n",hr);
}
CoTaskMemFree(szGUIDString);
}
}
if (pNewObject)
pNewObject->Release();
}
VariantClear(&var);
}
if (pIADsObject)
pIADsObject->Release();
if (pDispNewObject)
pDispNewObject->Release();
}
//Ask user if they want us to delete the test containers.
wprintf(L"Do you want to delete the test container and object (Y/N):");
_getws(pszBuffer);
if (0==wcsnicmp(L"Y", pszBuffer,1))
{
//Delete the object
//Delete the container
hr = pNewContainer->Delete(L"container",szNewObjectRenameRDN);
if (SUCCEEDED(hr))
{
wprintf(L"Successfully deleted test object.\n");
wcscpy(szRelPath,L"cn=");
wcscat(szRelPath,szContainer);
//Delete the container
hr = pDomain->Delete(L"container",szRelPath);
if (SUCCEEDED(hr))
wprintf(L"Successfully deleted test container and its contents.\n");
else
wprintf(L"Failed to delete test container and its contents. hr: %x\n",hr);
}
else
wprintf(L"Failed to delete test container and its contents. hr: %x\n",hr);
}
if (pNewContainer)
pNewContainer->Release();
}
if (pDisp)
pDisp->Release();
}
if (pDomain)
pDomain->Release();
}
if (pObject)
pObject->Release();
//Uninitialize COM
CoUninitialize();
return;
}
HRESULT AddValueToOtherWKOProperty(LPOLESTR szContainerDN, //DN for container whose otherWellKnownObjects property to modify
LPGUID pWKOGUID, //WKO GUID for the well-known object.
LPOLESTR szWKOObjectDN //DN of the well-known object.
)
{
HRESULT hr = E_FAIL;
LPOLESTR szGUIDString = new OLECHAR[MAX_PATH];
LPOLESTR szDNwithOctetString = new OLECHAR[MAX_PATH*2];
DWORD dwReturn;
//Connection handle
LDAP *hConnect = NULL;
//Specify NULL to bind to a DC in the current computer's domain.
//LDAP_PORT is the default port, 389
hConnect = ldap_open(NULL, LDAP_PORT);
//Bind using the preferred authentication method on Windows 2000
//and the caller's security context.
dwReturn = ldap_bind_s( hConnect, NULL, NULL, LDAP_AUTH_NEGOTIATE );
if (dwReturn==LDAP_SUCCESS)
{
//Create the WKO value to add.
GUIDtoBindableString (pWKOGUID, &szGUIDString);
DWORD dwGUIDSize = (wcslen(szGUIDString));
//Build the DNwithoctetstring
swprintf(szDNwithOctetString, L"B:%d:%s:%s", dwGUIDSize, szGUIDString,szWKOObjectDN);
// ULONG ulBerSize = (wcslen(szDNwithOctetString));
//Build the BerVal
// PCHAR pByteVal = (PCHAR)szDNwithOctetString;
// berval berWKO;
// berWKO.bv_len = ulBerSize;
// berWKO.bv_val = pByteVal;
//Build the mod structure to add the value.
LDAPMod ldWKO;
//mod_values takes a NULL terminated array of WCHARs.
//We're adding a single value.
WCHAR *StrValues[] = {szDNwithOctetString , NULL };
//Operation
ldWKO.mod_op = LDAP_MOD_ADD;
//Attribute
ldWKO.mod_type = L"otherWellKnownObjects";
//Value to set.
ldWKO.mod_vals.modv_strvals = StrValues;
//mods is a NULL terminated array of LDAPMod structures.
//We're adding a single value.
LDAPMod *pMod[] = {&ldWKO,NULL};
//Modify the object specified by szContainerDN.
dwReturn = ldap_modify_s( hConnect,
szContainerDN,
pMod);
CoTaskMemFree(szGUIDString);
if (dwReturn==LDAP_SUCCESS)
hr = S_OK;
}
return hr;
}
HRESULT GUIDtoBindableString (LPGUID pGUID, LPOLESTR *ppGUIDString)
{
HRESULT hr = E_FAIL;
if (!pGUID)
return E_INVALIDARG;
//Build bindable GUID string
LPOLESTR szDSGUID = new WCHAR [128];
DWORD dwLen = sizeof(*pGUID);
LPBYTE lpByte = (LPBYTE) pGUID;
//Copy a blank string to make it a zero length string.
wcscpy( szDSGUID, L"" );
//Loop through to add each byte to the string.
for( DWORD dwItem = 0L; dwItem < dwLen ; dwItem++ )
{
//Append to szDSGUID, double-byte, byte at dwItem index.
swprintf(szDSGUID + wcslen(szDSGUID), L"%02x", lpByte[dwItem]);
if( wcslen( szDSGUID ) > 128 )
break;
}
//Allocate memory for string
*ppGUIDString = (OLECHAR *)CoTaskMemAlloc (sizeof(OLECHAR)*(wcslen(szDSGUID)+1));
if (*ppGUIDString)
wcscpy(*ppGUIDString, szDSGUID);
else
hr=E_FAIL;
//Caller must free ppGUIDString using CoTaskMemFree.
return hr;
}
// This function gets the specified well-known object for the current user's domain.
HRESULT GetWKOObject(LPOLESTR szBindableWKGUID, //IN. Bindable string GUID of well-known object.
IADs **ppObject //OUT. Return a pointer to the specified well-known object.
)
{
HRESULT hr = E_FAIL;
//Get rootDSE and the domain container's DN.
IADs *pObject = NULL;
LPOLESTR szPath = new OLECHAR[MAX_PATH];
VARIANT var;
hr = ADsOpenObject(L"LDAP://rootDSE",
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
IID_IADs,
(void**)&pObject);
//Get current domain DN.
if (SUCCEEDED(hr))
{
hr = pObject->Get(L"defaultNamingContext",&var);
if (SUCCEEDED(hr))
{
//Build the WKGUID binding string.
wcscpy(szPath,L"LDAP://");
wcscat(szPath,L"<WKGUID=");
wcscat(szPath,szBindableWKGUID);
wcscat(szPath,L",");
wcscat(szPath,var.bstrVal);
wcscat(szPath,L">");
//Print the binding string.
//wprintf(L"WKGUID binding string: %s\n",szPath);
VariantClear(&var);
//Bind to the well-known object.
hr = ADsOpenObject(szPath,
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
IID_IADs,
(void**)ppObject);
if (FAILED(hr))
{
if (*ppObject)
{
(*ppObject)->Release();
(*ppObject) = NULL;
}
}
}
}
if (pObject)
pObject->Release();
return hr;
}