Adding Objects

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.

© 1998 by Wrox Press. All rights reserved.