Actually, adding objects is simplicity itself. The code that sits underneath the New… button in our application simply passes the current pathname to a new dialog:
void CDirManagerDlg::OnNew()
{
// Get current container path
CString csPath;
GetDlgItemText(IDC_PATH, csPath);
// Pass in to new object dialog
CNewObjectDlg dlg(csPath);
dlg.DoModal();
// Re-enumerate in case something has been added
OnEnumerate();
}
The dialog, which will appear when the New… button is clicked, looks like this:
The edit boxes have been called IDC_CONTAINER
, IDC_OBJECT
and IDC_CLASS
respectively. The code for the OnInitDialog()
method is shown below:
BOOL CNewObjectDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Outputting incoming container name
SetDlgItemText(IDC_CONTAINER, m_csContainer);
// Get container object
HRESULT hr = ADsGetObject (_bstr_t(m_csContainer), IID_IADsContainer,
reinterpret_cast<void**>(&m_pContainer));
if (FAILED (hr))
{
DisplayComError(_T("Failed to get container"), hr);
EndDialog (IDCANCEL);
return TRUE;
}
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
Here's the constructor:
CNewObjectDlg::CNewObjectDlg(const CString& csContainer,
CWnd* pParent /*=NULL*/)
: CDialog(CNewObjectDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CNewObjectDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
m_csContainer = csContainer;
}
This simply initializes the two private members in CNewObjectDlg
:
CString m_csContainer;
IADsContainerPtr m_pContainer;
And this is the business end of the code:
void CNewObjectDlg::OnOK()
{
// Get name and class of new object
CString csObject;
GetDlgItemText(IDC_OBJECT, csObject);
CString csClass;
GetDlgItemText(IDC_CLASS, csClass);
// Create new object with specified class in this container
IDispatchPtr pDispatch;
HRESULT hr = m_pContainer->Create(_bstr_t(csClass), _bstr_t(csObject),
&pDispatch);
if (FAILED(hr))
{
DisplayComError(_T("Failed to create new object"), hr);
return;
}
// Get main ADS interface for new object
IADsPtr pNewObject;
hr = pDispatch->QueryInterface(IID_IADs,
reinterpret_cast<void**>(&pNewObject));
if (FAILED(hr))
{
DisplayComError(_T("Failed to QI for IADs"), hr);
return;
}
// Confirm settings
pNewObject->SetInfo();
CDialog::OnOK();
}
The critical points here are the use of the Create()
method on the IADsContainer
interface to create our new object with the required class, and the use of the SetInfo()
method on the IADs
interface of the new object in order to commit the change to the directory. In fact, we haven't set up any of the properties of the object in this implementation, but if we choose our object carefully, it won't matter. Try, for example, creating an object of class User
with name, say, Wallace
. Lo and behold, it appears in the list when we complete the dialog. Moreover, it's still there when we look in the user administration facility. I should perhaps add that this will only work if the current user has sufficient privilege to add users — which is at least reassuring from the security point of view!
Now for the big question: can you create new classes? In other words, can you extend the schema? Well, in theory, all you have to do is create an object of type "Class", or indeed "Property" or "Syntax". The trouble is that if you try this, you run up against an error in the WinNT:
namespace. In LDAP, it's a different matter altogether, as we'll see shortly.