/////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1998, Microsoft Corp. All rights reserved. // // FILE // // dsobject.cpp // // SYNOPSIS // // This file defines the class DSObject. // // MODIFICATION HISTORY // // 02/20/1998 Original version. // 06/09/1998 Refresh property cache before enumerating dirty objects. // 02/11/1999 Keep downlevel parameters in sync. // 03/16/1999 Return error if downlevel update fails. // /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include WCHAR USER_PARAMETERS_NAME[] = L"UserParameters"; // Smart pointer for an IADsPropertyEntryPtr. _COM_SMARTPTR_TYPEDEF(IADsPropertyEntry, __uuidof(IADsPropertyEntry)); ////////// // Prefix added to all RDN's. ////////// _bstr_t DSObject::thePrefix(L"CN="); ////////// // The name of the 'name' property. ////////// _bstr_t DSObject::theNameProperty(L"name"); _bstr_t DSObject::theUserParametersProperty(USER_PARAMETERS_NAME); DSObject::DSObject(IUnknown* subject) : oldParms(NULL) { _Module.Lock(); // All subjects must support IADs && IDirectoryObject. _com_util::CheckError(subject->QueryInterface( __uuidof(IADs), (PVOID*)&leaf )); _com_util::CheckError(Restore()); } DSObject::~DSObject() throw () { SysFreeString(oldParms); _Module.Unlock(); } ////////// // IUnknown implementation is copied from CComObject<>. ////////// STDMETHODIMP_(ULONG) DSObject::AddRef() { return InternalAddRef(); } STDMETHODIMP_(ULONG) DSObject::Release() { ULONG l = InternalRelease(); if (l == 0) { delete this; } return l; } STDMETHODIMP DSObject::QueryInterface(REFIID iid, void ** ppvObject) { return _InternalQueryInterface(iid, ppvObject); } STDMETHODIMP DSObject::get_Name(BSTR* pVal) { if (pVal == NULL) { return E_OUTOFMEMORY; } VARIANT v; RETURN_ERROR(leaf->Get(theNameProperty, &v)); // We should have gotten back a non-null BSTR. if (V_VT(&v) != VT_BSTR || V_BSTR(&v) == NULL) { return E_FAIL; } *pVal = V_BSTR(&v); return S_OK; } STDMETHODIMP DSObject::get_Class(BSTR* pVal) { return leaf->get_Class(pVal); } STDMETHODIMP DSObject::get_GUID(BSTR* pVal) { return leaf->get_GUID(pVal); } STDMETHODIMP DSObject::get_Container(IDataStoreContainer** pVal) { return E_NOTIMPL; } STDMETHODIMP DSObject::GetValue(BSTR bstrName, VARIANT* pVal) { return leaf->Get(bstrName, pVal); } STDMETHODIMP DSObject::GetValueEx(BSTR bstrName, VARIANT* pVal) { return leaf->GetEx(bstrName, pVal); } STDMETHODIMP DSObject::PutValue(BSTR bstrName, VARIANT* pVal) { if (pVal == NULL) { return E_INVALIDARG; } // Flag the object as dirty. dirty = TRUE; // Synch up the downlevel parameters. downlevel.PutValue(bstrName, pVal); if ( VT_EMPTY == V_VT(pVal) ) { return leaf->PutEx(ADS_PROPERTY_CLEAR, bstrName, *pVal); } else if ( VT_ARRAY == (V_VT(pVal) & VT_ARRAY) ) { return leaf->PutEx(ADS_PROPERTY_UPDATE, bstrName, *pVal); } else { return leaf->Put(bstrName, *pVal); } } STDMETHODIMP DSObject::Update() { // Update the UserParameters. PWSTR newParms; HRESULT hr = downlevel.Update(oldParms, &newParms); if (FAILED(hr)) { return hr; } // Convert to a VARIANT. VARIANT value; VariantInit(&value); V_VT(&value) = VT_BSTR; V_BSTR(&value) = SysAllocString(newParms); // Set the UserParameters property. leaf->Put(theUserParametersProperty, value); // Clean-up. VariantClear(&value); LocalFree(newParms); return leaf->SetInfo(); } STDMETHODIMP DSObject::Restore() { // Free the old UserParameters. if (oldParms) { SysFreeString(oldParms); oldParms = NULL; } dirty = FALSE; HRESULT hr = leaf->GetInfo(); if (SUCCEEDED(hr)) { // Read the UserParameters property. VARIANT value; if (leaf->Get(theUserParametersProperty, &value) == S_OK) { if (V_VT(&value) == VT_BSTR) { oldParms = V_BSTR(&value); } else { // This should never happen. VariantClear(&value); } } } else if (hr == E_ADS_OBJECT_UNBOUND) { hr = S_OK; } return hr; } STDMETHODIMP DSObject::Item(BSTR bstrName, IDataStoreProperty** pVal) { if (bstrName == NULL || pVal == NULL) { return E_INVALIDARG; } *pVal = NULL; // Get the value for this item. _variant_t value; RETURN_ERROR(leaf->Get(bstrName, &value)); try { // Create a new property object. (*pVal = new MyProperty(bstrName, value, this))->AddRef(); } CATCH_AND_RETURN() return S_OK; } STDMETHODIMP DSObject::get_PropertyCount(long* pVal) { if (pVal == NULL) { return E_INVALIDARG; } if (dirty) { RETURN_ERROR(Restore()); } MyProperties properties(leaf); if (!properties) { // Some ADSI providers may not implement IADsPropertyList. *pVal = 0; return E_NOTIMPL; } return properties->get_PropertyCount(pVal); } STDMETHODIMP DSObject::get_NewPropertyEnum(IUnknown** pVal) { if (pVal == NULL) { return E_INVALIDARG; } *pVal = NULL; if (dirty) { RETURN_ERROR(Restore()); } MyProperties properties(leaf); // Some ADSI providers may not implement IADsPropertyList. if (!properties) { return E_NOTIMPL; } // Reset the list in case this isn't the first time we've enumerated it. properties->Reset(); try { using _com_util::CheckError; // How many properties are there? long count; CheckError(properties->get_PropertyCount(&count)); // Create a temporary array of items. std::vector<_variant_t> items; items.reserve(count); ////////// // Load all the properties into the temporary array. ////////// while (count--) { // Get the next item in the list. _variant_t item; CheckError(properties->Next(&item)); // Convert it to a Property Entry. IADsPropertyEntryPtr entry(item); // Get the property name. BSTR bstrName; CheckError(entry->get_Name(&bstrName)); _bstr_t name(bstrName, false); // Get the property value. _variant_t value; HRESULT hr = leaf->Get(name, &value); if (FAILED(hr)) { if (hr == E_ADS_CANT_CONVERT_DATATYPE) { // This must be one of those nasty NTDS attributes that has // no VARIANT representation. continue; } _com_issue_error(hr); } // Create the property object and add it to the vector. items.push_back(new MyProperty(name, value, this)); } ////////// // Create and initialize an enumerator for the items. ////////// CComPtr newEnum(new CComObject); _com_util::CheckError(newEnum->Init(items.begin(), items.end(), NULL, AtlFlagCopy)); // Return it to the caller. (*pVal = newEnum)->AddRef(); } CATCH_AND_RETURN() return S_OK; } STDMETHODIMP DSObject::Item(BSTR bstrName, IDataStoreObject** ppObject) { if (ppObject == NULL) { return E_INVALIDARG; } try { // Get the ADSI object. CComPtr disp; _com_util::CheckError(node->GetObject(NULL, thePrefix + bstrName, &disp)); // Convert to a DSObject. *ppObject = spawn(disp); } CATCH_AND_RETURN() return S_OK; } STDMETHODIMP DSObject::Create(BSTR bstrClass, BSTR bstrName, IDataStoreObject** ppObject) { if (ppObject == NULL) { return E_INVALIDARG; } try { // Create the ADSI object. CComPtr disp; _com_util::CheckError(node->Create(bstrClass, thePrefix + bstrName, &disp)); // Convert to a DSObject. *ppObject = spawn(disp); } CATCH_AND_RETURN() return S_OK; } STDMETHODIMP DSObject::MoveHere(IDataStoreObject* pObject, BSTR bstrNewName) { if (pObject == NULL) { return E_INVALIDARG; } try { using _com_util::CheckError; // Downcast to a DSObject. DSObject* obj = DSObject::narrow(pObject); // Get the absolute path of the object being moved. CComBSTR path; CheckError(obj->leaf->get_ADsPath(&path)); // Is the object being renamed? _bstr_t newName(bstrNewName ? thePrefix + bstrNewName : _bstr_t()); // Move it to this container. CComPtr disp; CheckError(node->MoveHere(path, newName, &disp)); ////////// // Set the leaf to the new object. ////////// CComPtr ads; CheckError(disp->QueryInterface(__uuidof(IADs), (PVOID*)&ads)); obj->leaf.Release(); obj->leaf = ads; } CATCH_AND_RETURN() return S_OK; } STDMETHODIMP DSObject::Remove(BSTR bstrClass, BSTR bstrName) { if (bstrClass == NULL) { return E_INVALIDARG; } try { _com_util::CheckError(node->Delete(bstrClass, thePrefix + bstrName)); } CATCH_AND_RETURN() return S_OK; } STDMETHODIMP DSObject::get_ChildCount(long *pVal) { return node->get_Count(pVal); } STDMETHODIMP DSObject::get_NewChildEnum(IUnknown** pVal) { if (pVal == NULL) { return E_INVALIDARG; } *pVal = NULL; try { // Get the ADSI enumerator. CComPtr unk; _com_util::CheckError(node->get__NewEnum(&unk)); // Convert to an IEnumVARIANT. CComPtr enumVariant; _com_util::CheckError(unk->QueryInterface(__uuidof(IEnumVARIANT), (PVOID*)&enumVariant)); // Construct our wrapper around the real enumerator. (*pVal = new DSEnumerator(this, enumVariant))->AddRef(); } CATCH_AND_RETURN() return S_OK; } IDataStoreObject* DSObject::spawn(IUnknown* subject) { DSObject* child = new DSObject(subject); child->InternalAddRef(); return child; } DSObject* DSObject::narrow(IUnknown* p) { DSObject* object; using _com_util::CheckError; CheckError(p->QueryInterface(__uuidof(DSObject), (PVOID*)&object)); // We can get away with InternalRelease since the caller must still // have a reference to this object. object->InternalRelease(); return object; } HRESULT WINAPI DSObject::getContainer(void* pv, REFIID, LPVOID* ppv, DWORD_PTR) { DSObject* obj = (DSObject*)pv; // If we don't have a node pointer, try to get one. if (obj->node == NULL) { obj->leaf->QueryInterface(__uuidof(IADsContainer), (PVOID*)&obj->node); } // If node is not NULL, then we are a container. if (obj->node != NULL) { *ppv = (IDataStoreContainer*)obj; obj->AddRef(); return S_OK; } *ppv = NULL; return E_NOINTERFACE; }