Let's create a new dialog-based MFC application using the MFC AppWizard. We'll start off by simply dumping out a list of all the objects in a container.
I've called the project DirManager
. The main dialog is called IDD_DIRMANAGER_DIALOG
, and its user interface is pretty simple. There are four buttons:
IDC_ENUMERATE
(the default)
IDC_NEW
IDC_CLASS
IDOK
We'll see what these buttons do as we work our way through the application. There's a single-line edit field, IDC_PATH
, which is used for inputting an Active Directory path, and a listbox, IDC_OBJECTS
, which is used to display all the objects found starting from that path.
The dialog is shown below:
Don't worry about any of the buttons apart from Enumerate for the time being. We'll start with this one. The user should type the starting container in the edit field at the top, and when the Enumerate button is pressed, we'll enumerate all the objects in the specified container and output the names to the list box below. As well as their names, we're also going to display their Active Directory pathnames; this is a slightly sneaky way of making sure that our application has ready access to the pathnames, so we don't have to hold boring arrays of object interface pointers.
Since we'll be using Visual C++'s COM smart pointers, you'll need to include comdef.h
in StdAfx.h
, along with our simplified typedef
macro:
#include <comdef.h>
#define SMARTPTR_TYPEDEF(x) \
typedef _com_ptr_t<_com_IIID<x, &IID_##x> > x##Ptr;
You'll need to reference one of the header files from the Platform SDK at the top of CDirManagerDlg.cpp
, and also define typedef
s for the interfaces that we'll be using:
#include "activeds.h"
SMARTPTR_TYPEDEF(IADsContainer);
SMARTPTR_TYPEDEF(IADs);
SMARTPTR_TYPEDEF(IADsProperty);
SMARTPTR_TYPEDEF(IADsSyntax);
Here is the first part of the code that gets executed when the user hits Enumerate:
void CDirManagerDlg::OnEnumerate()
{
CListBox* pObjects = static_cast<CListBox*>(GetDlgItem(IDC_OBJECTS));
pObjects->ResetContent();
// Get path from user, and get corresponding container object
CString csPath;
GetDlgItemText(IDC_PATH, csPath);
IADsContainerPtr pContainer;
HRESULT hr = ADsGetObject(_bstr_t(csPath), IID_IADsContainer,
reinterpret_cast<void**>(&pContainer));
if (FAILED(hr))
{
DisplayComError(_T("Failed to get object"), hr);
return;
}
ADsGetObject()
is a neat little helper function that gets hold of an ADSI interface on an object given its path. You might be interested to know that it does this by means of a moniker. (Remember the fun we had with those in Chapter 2?) ADsGetObject()
actually uses our old friend MkParseDisplayName()
to get hold of a moniker for the object based on its path, and then it calls BindToObject()
to get hold of the ADSI interface. In this case, it's the ADSI provider itself that is implementing IParseDisplayName
.
There are several other helper functions provided with ADSI. In fact, here are a couple more:
ADsBuildEnumerator()
ADsEnumerateNext()
We'll use these to enumerate all the objects in our container. (Note that we could use get__NewEnum()
to get a new enumerator object, and then use the IEnumVARIANT
interface of the object directly, but then why bother if ADSI is going to do the hard work for us?)
In the call to ADsEnumerateNext()
, the value of 1
for the second argument indicates that we want to fetch just one object at a time, and the NULL
for the fourth argument indicates that we aren't interested in finding out how many were actually found (we're using the HRESULT
for that).
// Construct enumerator
IEnumVARIANTPtr pEnum;
hr = ADsBuildEnumerator(pContainer, &pEnum);
if (FAILED(hr))
{
DisplayComError(_T("Failed to build enumerator"), hr);
return;
}
// Go through list of children
while (true)
{
_variant_t vChild;
hr = ADsEnumerateNext(pEnum, 1, &vChild, NULL);
if (hr != S_OK)
break;
// Get main ADS interface for child
IADsPtr pChild;
hr = vChild.pdispVal->QueryInterface(IID_IADs,
reinterpret_cast<void**>(&pChild));
if (FAILED(hr))
{
DisplayComError(_T("Failed to QI for child"), hr);
continue;
}
At this point, we've got our IADs
interface, so we can interrogate it for its name and path:
// Get name ...
BSTR bstrName;
pChild->get_Name(&bstrName);
CString csName = bstrName;
SysFreeString(bstrName);
// ... and path
BSTR bstrChildPath;
pChild->get_ADsPath(&bstrChildPath);
CString csChildPath = bstrChildPath;
SysFreeString(bstrChildPath);
// Construct entry for list and add to listbox
CString csItem;
csItem.Format("%s (%s)", static_cast<LPCSTR>(csName),
static_cast<LPCSTR>(csChildPath));
pObjects->AddString(csItem);
}
}