2553 lines
63 KiB
C++
2553 lines
63 KiB
C++
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
// Copyright (C) Microsoft Corporation, 1992 - 2000.
|
||
|
//
|
||
|
// File: cumiobj.cxx
|
||
|
//
|
||
|
// Contents: Contains the implementation of IUmiObject/Container methods.
|
||
|
// Methods are encapsulated in one object but this object holds
|
||
|
// a pointer to the inner unknown of the corresponding LDAP
|
||
|
// object. The methods of IUmiContainer are also implemented on
|
||
|
// this object, but will only be used if the underlying object
|
||
|
// is a container.
|
||
|
//
|
||
|
// History: 03-06-00 SivaramR Created.
|
||
|
// 04-07-00 AjayR modified for LDAP Provider.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
#include "ldap.hxx"
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::CLDAPUmiObject
|
||
|
//
|
||
|
// Synopsis: Constructor
|
||
|
//
|
||
|
// Arguments: None
|
||
|
//
|
||
|
// Returns: N/A
|
||
|
//
|
||
|
// Modifies: N/A
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
CLDAPUmiObject::CLDAPUmiObject():
|
||
|
_pPropMgr(NULL),
|
||
|
_pIntfPropMgr(NULL),
|
||
|
_pUnkInner(NULL),
|
||
|
_pIADs(NULL),
|
||
|
_pIADsContainer(NULL),
|
||
|
_ulErrorStatus(0),
|
||
|
_pCoreObj(NULL),
|
||
|
_pExtMgr(NULL),
|
||
|
_fOuterUnkSet(FALSE)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::~CLDAPUmiObject
|
||
|
//
|
||
|
// Synopsis: Destructor
|
||
|
//
|
||
|
// Arguments: None
|
||
|
//
|
||
|
// Returns: N/A
|
||
|
//
|
||
|
// Modifies: N/A
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
CLDAPUmiObject::~CLDAPUmiObject(void)
|
||
|
{
|
||
|
if (_pIntfPropMgr) {
|
||
|
delete _pIntfPropMgr;
|
||
|
}
|
||
|
|
||
|
if (_pPropMgr) {
|
||
|
delete _pPropMgr;
|
||
|
}
|
||
|
|
||
|
if (_pUnkInner) {
|
||
|
_pUnkInner->Release();
|
||
|
}
|
||
|
|
||
|
if (_pIADsContainer) {
|
||
|
_pIADsContainer->Release();
|
||
|
}
|
||
|
|
||
|
if (_pIADs) {
|
||
|
_pIADs->Release();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We specifically do not release these as they are all just ptrs
|
||
|
// _pCreds, _pszLDAPServer, _pszLDAPDn, _pLdapHandle.
|
||
|
//
|
||
|
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::AllocateLDAPUmiObject --- Static constructor.
|
||
|
//
|
||
|
// Synopsis: Static contstructor routine.
|
||
|
//
|
||
|
// Arguments: intfPropTable- Schema information for interface properties.
|
||
|
// pPropCache - Pointer to property cache (shared with IADs).
|
||
|
// pUnkInner - Pointer to inner unknown of underlying obj.
|
||
|
// pExtMgr - Pointer to extension manager of object.
|
||
|
// pCoreObj - Pointer to the core object of underlying object.
|
||
|
// ppUmiObj - Return value.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: *ppUmObj to point to newly created object.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
HRESULT
|
||
|
CLDAPUmiObject::CreateLDAPUmiObject(
|
||
|
INTF_PROP_DATA intfPropTable[],
|
||
|
CPropertyCache *pPropertyCache,
|
||
|
IUnknown *pUnkInner,
|
||
|
CCoreADsObject *pCoreObj,
|
||
|
IADs *pIADs,
|
||
|
CCredentials *pCreds,
|
||
|
CLDAPUmiObject **ppUmiObj,
|
||
|
DWORD dwPort, // defaulted to -1
|
||
|
PADSLDP pLdapHandle, // defaulted to NULL
|
||
|
LPWSTR pszServerName, // defaulted to NULL
|
||
|
LPWSTR pszLDAPDn, // defaulted to NULL
|
||
|
CADsExtMgr *pExtMgr // defaulted to NULL
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
CLDAPUmiObject *pUmiObject = NULL;
|
||
|
CPropertyManager *pPropMgr = NULL;
|
||
|
CPropertyManager *pIntfPropMgr = NULL;
|
||
|
|
||
|
//
|
||
|
// This always has to be there, the extension manager will not
|
||
|
// be there for RootDSE and schema realted objects.
|
||
|
//
|
||
|
ADsAssert(pCoreObj);
|
||
|
|
||
|
pUmiObject = new CLDAPUmiObject();
|
||
|
|
||
|
if (!pUmiObject) {
|
||
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Property cache is not supported on the schema container object alone.
|
||
|
// There will be no support for the propList methods for this object.
|
||
|
//
|
||
|
if (pPropertyCache) {
|
||
|
//
|
||
|
// Has to be valid if we have a property cache.
|
||
|
//
|
||
|
ADsAssert(pUnkInner);
|
||
|
|
||
|
//
|
||
|
// Need the standard property manager for Get/Put support.
|
||
|
//
|
||
|
hr = CPropertyManager::CreatePropertyManager(
|
||
|
pIADs,
|
||
|
(IUmiObject *) pUmiObject,
|
||
|
pPropertyCache,
|
||
|
pCreds,
|
||
|
pszServerName,
|
||
|
&pPropMgr
|
||
|
);
|
||
|
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
}
|
||
|
|
||
|
if (intfPropTable) {
|
||
|
hr = CPropertyManager::CreatePropertyManager(
|
||
|
(IUmiObject *) pUmiObject,
|
||
|
pUnkInner, // should support IADs.
|
||
|
pCreds,
|
||
|
intfPropTable,
|
||
|
&pIntfPropMgr
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// At this point failures are not catastrophic so we can prepare
|
||
|
// the object for return.
|
||
|
//
|
||
|
pUmiObject->_pUnkInner = pUnkInner;
|
||
|
pUmiObject->_pIADs = pIADs;
|
||
|
pUmiObject->_pExtMgr = pExtMgr;
|
||
|
pUmiObject->_pCoreObj = pCoreObj;
|
||
|
pUmiObject->_pCreds = pCreds;
|
||
|
pUmiObject->_pPropMgr = pPropMgr;
|
||
|
pUmiObject->_pIntfPropMgr = pIntfPropMgr;
|
||
|
pUmiObject->_pszLDAPServer = pszServerName;
|
||
|
pUmiObject->_pszLDAPDn = pszLDAPDn;
|
||
|
pUmiObject->_pLdapHandle = pLdapHandle;
|
||
|
pUmiObject->_dwPort = dwPort;
|
||
|
|
||
|
//
|
||
|
// Addref cause we release this in the destructor.
|
||
|
//
|
||
|
pIADs->AddRef();
|
||
|
|
||
|
//
|
||
|
// Get IADsContainer ptr if applicable - can ignore failures.
|
||
|
//
|
||
|
hr = pUnkInner->QueryInterface(
|
||
|
IID_IADsContainer,
|
||
|
(void **) &(pUmiObject->_pIADsContainer)
|
||
|
);
|
||
|
|
||
|
*ppUmiObj = pUmiObject;
|
||
|
|
||
|
RRETURN(S_OK);
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (pUmiObject) {
|
||
|
delete pUmiObject;
|
||
|
}
|
||
|
|
||
|
if (pPropMgr) {
|
||
|
delete pPropMgr;
|
||
|
}
|
||
|
|
||
|
if (pIntfPropMgr) {
|
||
|
delete pIntfPropMgr;
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::QueryInterface --- IUnknown support.
|
||
|
//
|
||
|
// Synopsis: Standard query interface method.
|
||
|
//
|
||
|
// Arguments: iid - Interface requested.
|
||
|
// ppInterface - Return pointer to interface requested.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: *ppInterface to return interface pointer.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::QueryInterface(
|
||
|
REFIID iid,
|
||
|
LPVOID *ppInterface
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
IUnknown *pTmpIntfPtr = NULL;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
if (!ppInterface) {
|
||
|
RRETURN(E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
*ppInterface = NULL;
|
||
|
|
||
|
if (IsEqualIID(iid, IID_IUnknown)) {
|
||
|
*ppInterface = (IUmiObject *) this;
|
||
|
}
|
||
|
else if (IsEqualIID(iid, IID_IUmiPropList)) {
|
||
|
*ppInterface = (IUmiObject *) this;
|
||
|
}
|
||
|
else if (IsEqualIID(iid, IID_IUmiBaseObject)) {
|
||
|
*ppInterface = (IUmiObject *) this;
|
||
|
}
|
||
|
else if (IsEqualIID(iid, IID_IUmiObject)) {
|
||
|
*ppInterface = (IUmiObject *) this;
|
||
|
}
|
||
|
else if (IsEqualIID(iid, IID_IUmiContainer)) {
|
||
|
//
|
||
|
// Check if underlying LDAP object is a container.
|
||
|
//
|
||
|
if (_pIADsContainer != NULL) {
|
||
|
*ppInterface = (IUmiContainer *) this;
|
||
|
}
|
||
|
else {
|
||
|
RRETURN(E_NOINTERFACE);
|
||
|
}
|
||
|
}
|
||
|
else if (IsEqualIID(iid, IID_IUmiCustomInterfaceFactory)) {
|
||
|
*ppInterface = (IUmiCustomInterfaceFactory *) this;
|
||
|
}
|
||
|
else if (IsEqualIID(iid, IID_IADsObjOptPrivate)) {
|
||
|
if (_pLdapHandle) {
|
||
|
*ppInterface = (IADsObjOptPrivate*) this;
|
||
|
}
|
||
|
else {
|
||
|
RRETURN(E_NOINTERFACE);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
RRETURN(E_NOINTERFACE);
|
||
|
}
|
||
|
|
||
|
AddRef();
|
||
|
RRETURN(S_OK);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::Clone --- IUmiObject support.
|
||
|
//
|
||
|
// Synopsis: Clones this object. Note that this will do an implicit
|
||
|
// refresh if one has not already been done on the object before
|
||
|
// copying all the attributes. The new object has a state that a
|
||
|
// bound state (rather than unbound).
|
||
|
//
|
||
|
// Arguments: uFlags --- Must be 0 for now.
|
||
|
// riid --- IID requested on the clone.
|
||
|
// pCopy --- The return ptr for the cloned object.
|
||
|
//
|
||
|
// Returns: S_OK or appropriate error code.
|
||
|
//
|
||
|
// Modifies: pCopy to be updated with new UmiObject ptr on success.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::Clone(
|
||
|
ULONG uFlags,
|
||
|
REFIID riid,
|
||
|
LPVOID *pCopy
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
CCredentials Creds;
|
||
|
BSTR bstrPath = NULL;
|
||
|
BSTR bstrClass = NULL;
|
||
|
LPWSTR pszParent = NULL, pszCommonName = NULL;
|
||
|
IUmiObject *pUmiObj = NULL;
|
||
|
IUmiObject *pDestUmiObj = NULL;
|
||
|
DWORD dwAuthFlags = 0;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
//
|
||
|
// We cannot clone objects that do not have an underlying IADs ptr.
|
||
|
//
|
||
|
if (!_pIADs) {
|
||
|
BAIL_ON_FAILURE(hr = E_FAIL);
|
||
|
}
|
||
|
|
||
|
if (_pCreds) {
|
||
|
Creds = *_pCreds;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Need to tag on the ADS_FAST_BIND to prevent the call from
|
||
|
// going on the wire.
|
||
|
//
|
||
|
dwAuthFlags = Creds.GetAuthFlags();
|
||
|
Creds.SetAuthFlags(dwAuthFlags | ADS_FAST_BIND);
|
||
|
|
||
|
//
|
||
|
// Need to call Refresh with the internal flag. Note that using this
|
||
|
// flag means that we will go on the wire only if we have to.
|
||
|
//
|
||
|
hr = this->Refresh(
|
||
|
ADSI_INTERNAL_FLAG_GETINFO_AS_NEEDED,
|
||
|
NULL,
|
||
|
NULL
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
//
|
||
|
// Next we need to get the destination object.
|
||
|
//
|
||
|
hr = _pIADs->get_ADsPath(&bstrPath);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
if (_pCoreObj->GetObjectState() == ADS_OBJECT_UNBOUND) {
|
||
|
//
|
||
|
// In this case we need to Create the target object.
|
||
|
// The parent path, the common name and the class are needed.
|
||
|
//
|
||
|
hr = BuildADsParentPath(
|
||
|
bstrPath,
|
||
|
&pszParent,
|
||
|
&pszCommonName
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
hr = _pIADs->get_Class(&bstrClass);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
hr = CLDAPGenObject::CreateGenericObject(
|
||
|
pszParent,
|
||
|
pszCommonName,
|
||
|
bstrClass,
|
||
|
Creds,
|
||
|
ADS_OBJECT_UNBOUND,
|
||
|
riid, // this is ignored
|
||
|
(void **) &pUmiObj
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
}
|
||
|
else {
|
||
|
//
|
||
|
// In this case we bind to the object.
|
||
|
//
|
||
|
hr = GetObject(bstrPath, Creds, (void **)&pUmiObj);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
}
|
||
|
|
||
|
pUmiObj->QueryInterface(IID_IUmiObject, (void **) &pDestUmiObj);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
//
|
||
|
// Now we can call the helper to copy the attributes over.
|
||
|
//
|
||
|
hr = this->CopyToHelper(
|
||
|
(IUmiObject*) this,
|
||
|
pDestUmiObj,
|
||
|
0,
|
||
|
FALSE, // do not mark as update
|
||
|
FALSE // do not copy intf props
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
//
|
||
|
// Update return value as this means copy was succesful.
|
||
|
//
|
||
|
*pCopy = pDestUmiObj;
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
if (!_ulErrorStatus) {
|
||
|
SetLastStatus(hr);
|
||
|
}
|
||
|
hr = MapHrToUmiError(hr);
|
||
|
|
||
|
if (pDestUmiObj) {
|
||
|
pDestUmiObj->Release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (bstrPath) {
|
||
|
SysFreeString(bstrPath);
|
||
|
}
|
||
|
|
||
|
if (bstrClass) {
|
||
|
SysFreeString(bstrClass);
|
||
|
}
|
||
|
|
||
|
if (pszParent) {
|
||
|
FreeADsStr(pszParent);
|
||
|
}
|
||
|
|
||
|
if (pszCommonName) {
|
||
|
FreeADsStr(pszCommonName);
|
||
|
}
|
||
|
|
||
|
if (pUmiObj) {
|
||
|
pUmiObj->Release();
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::CopyTo --- IUmiObject support.
|
||
|
//
|
||
|
// Synopsis: Copies the object to the new destination.Note that this will
|
||
|
// do an implicit refresh if one has not already been done on the
|
||
|
// object before copying all the attributes. The new objecst state
|
||
|
// is unbound, and the new object will be created on the destination
|
||
|
// directory only when Commit is called. If any of the properties on
|
||
|
// the object being copied are marked as updated/dirty then the call
|
||
|
// will fail.
|
||
|
//
|
||
|
// Arguments: uFlags --- Must be 0 for now.
|
||
|
// pURL --- Url pointing to the destination.
|
||
|
// riid --- IID requested on the clone.
|
||
|
// pCopy --- The return ptr for the copied object.
|
||
|
//
|
||
|
// Returns: S_OK or appropriate error code.
|
||
|
//
|
||
|
// Modifies: pCopy to be updated with new UmiObject ptr on success.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::CopyTo(
|
||
|
IN ULONG uFlags,
|
||
|
IN IUmiURL *pURL,
|
||
|
IN REFIID riid,
|
||
|
OUT LPVOID *pCopy
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
CCredentials Creds;
|
||
|
BSTR bstrClass = NULL;
|
||
|
LPWSTR pszLdapPath = NULL;
|
||
|
LPWSTR pszParent = NULL, pszCommonName = NULL;
|
||
|
IUmiObject *pUmiObj = NULL;
|
||
|
IUmiObject *pUmiDestObj = NULL;
|
||
|
DWORD dwAuthFlags = 0;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
RRETURN(E_NOTIMPL);
|
||
|
//*******************************************************/
|
||
|
// This code is not used currently. /
|
||
|
//*******************************************************/
|
||
|
|
||
|
//
|
||
|
// Has to be 0.
|
||
|
//
|
||
|
if (uFlags) {
|
||
|
BAIL_ON_FAILURE(hr = UMI_E_INVALID_FLAGS);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// All these need to be valid.
|
||
|
//
|
||
|
if (!pURL
|
||
|
|| !pCopy
|
||
|
|| (riid != IID_IUmiObject)
|
||
|
) {
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We cannot copy objects that do not have an underlying IADs ptr.
|
||
|
//
|
||
|
if (!_pIADs) {
|
||
|
BAIL_ON_FAILURE(hr = E_FAIL);
|
||
|
}
|
||
|
|
||
|
if (_pCreds) {
|
||
|
Creds = *_pCreds;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Need to call Refresh with the internal flag. Note that using this
|
||
|
// flag means that we will go on the wire only if we have to.
|
||
|
//
|
||
|
hr = this->Refresh(
|
||
|
ADSI_INTERNAL_FLAG_GETINFO_AS_NEEDED,
|
||
|
NULL,
|
||
|
NULL
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
hr = _pIADs->get_Class(&bstrClass);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
//
|
||
|
// Now we need to convert the UmiPath to LDAPPath that we can use.
|
||
|
//
|
||
|
hr = UrlToLDAPPath(
|
||
|
pURL,
|
||
|
&pszLdapPath
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
//
|
||
|
// We need to split path to parent and common name.
|
||
|
//
|
||
|
hr = BuildADsParentPath(
|
||
|
pszLdapPath,
|
||
|
&pszParent,
|
||
|
&pszCommonName
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
hr = CLDAPGenObject::CreateGenericObject(
|
||
|
pszParent,
|
||
|
pszCommonName,
|
||
|
bstrClass,
|
||
|
Creds,
|
||
|
ADS_OBJECT_UNBOUND,
|
||
|
riid, // this is ignored
|
||
|
(void **) &pUmiObj
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
hr = pUmiObj->QueryInterface(riid, (void **) &pUmiDestObj);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
//
|
||
|
// Need to copy the attributes over now.
|
||
|
//
|
||
|
hr = this->CopyToHelper(
|
||
|
this,
|
||
|
pUmiDestObj,
|
||
|
0,
|
||
|
TRUE, // means fail call if property status is not 0
|
||
|
FALSE // do not copy intf props.
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
*pCopy = pUmiDestObj;
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (pszLdapPath) {
|
||
|
FreeADsStr(pszLdapPath);
|
||
|
}
|
||
|
|
||
|
if (pszParent) {
|
||
|
FreeADsStr(pszParent);
|
||
|
}
|
||
|
|
||
|
if (pszCommonName) {
|
||
|
FreeADsStr(pszCommonName);
|
||
|
}
|
||
|
|
||
|
if (pUmiObj) {
|
||
|
pUmiObj->Release();
|
||
|
}
|
||
|
|
||
|
if (bstrClass) {
|
||
|
SysFreeString(bstrClass);
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
if (!this->_ulErrorStatus) {
|
||
|
SetLastError(hr);
|
||
|
}
|
||
|
|
||
|
if (pUmiDestObj) {
|
||
|
pUmiDestObj->Release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::Refresh --- IUmiObject support.
|
||
|
//
|
||
|
// Synopsis: Refreshes the properties of the object. This calls the
|
||
|
// underlying LDAP object for the operation.
|
||
|
//
|
||
|
// Arguments: uFlags - UMI_FLAGS_REFERESH_ALL, PARTIAL supported
|
||
|
// and ADSI internal flag to implicit GetInfo only
|
||
|
// if one is needed (helps clone and copyto).
|
||
|
// uNameCount - Number of attributes to refresh.
|
||
|
// pszNames - Names of attributes to refresh.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise
|
||
|
//
|
||
|
// Modifies: Underlying property cache.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
HRESULT CLDAPUmiObject::Refresh(
|
||
|
ULONG uFlags,
|
||
|
ULONG uNameCount,
|
||
|
LPWSTR *pszNames
|
||
|
)
|
||
|
{
|
||
|
ULONG i = 0;
|
||
|
HRESULT hr = S_OK;
|
||
|
BOOL fUseGetInfoEx = FALSE;
|
||
|
DWORD dwGetInfoFlag = TRUE;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
//
|
||
|
// Only all and partial and the special internal flag. Refresh
|
||
|
// partial translates to an implicit getinfo in ADSI.
|
||
|
//
|
||
|
if ((uFlags != UMI_FLAG_REFRESH_ALL)
|
||
|
&& (uFlags != UMI_FLAG_REFRESH_PARTIAL)
|
||
|
&& (uFlags != ADSI_INTERNAL_FLAG_GETINFO_AS_NEEDED)
|
||
|
) {
|
||
|
BAIL_ON_FAILURE(hr = UMI_E_INVALID_FLAGS);
|
||
|
}
|
||
|
|
||
|
if (uFlags == UMI_FLAG_REFRESH_PARTIAL) {
|
||
|
//
|
||
|
// Cannot specify list of names in this case.
|
||
|
//
|
||
|
if (uNameCount != 0) {
|
||
|
BAIL_ON_FAILURE(hr = UMI_E_UNSUPPORTED_OPERATION);
|
||
|
}
|
||
|
dwGetInfoFlag = GETINFO_FLAG_IMPLICIT;
|
||
|
}
|
||
|
else if (uFlags == ADSI_INTERNAL_FLAG_GETINFO_AS_NEEDED) {
|
||
|
dwGetInfoFlag = GETINFO_FLAG_IMPLICIT_AS_NEEDED;
|
||
|
}
|
||
|
|
||
|
if ((uFlags == UMI_FLAG_REFRESH_ALL) && (uNameCount != 0)) {
|
||
|
fUseGetInfoEx = TRUE;
|
||
|
}
|
||
|
|
||
|
if (fUseGetInfoEx) {
|
||
|
//
|
||
|
// Build the variant array of strings.
|
||
|
//
|
||
|
VARIANT vVar;
|
||
|
VariantInit(&vVar);
|
||
|
|
||
|
//
|
||
|
// Builds a variant array we can use in GetInfoEx
|
||
|
//
|
||
|
hr = ADsBuildVarArrayStr(
|
||
|
pszNames,
|
||
|
(DWORD)uNameCount,
|
||
|
&vVar);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
//
|
||
|
// Call GetInfoEx to do the actual work.
|
||
|
//
|
||
|
hr = _pIADs->GetInfoEx(vVar, 0);
|
||
|
VariantClear(&vVar);
|
||
|
}
|
||
|
else {
|
||
|
hr = this->_pCoreObj->GetInfo(dwGetInfoFlag);
|
||
|
//
|
||
|
// Since schema and few others do not implement the implicit GetInfo.
|
||
|
//
|
||
|
if (FAILED(hr) && hr == E_NOTIMPL) {
|
||
|
//
|
||
|
// Should try just an ordinary GetInfo if applicable.
|
||
|
//
|
||
|
if (dwGetInfoFlag == GETINFO_FLAG_EXPLICIT) {
|
||
|
hr = _pIADs->GetInfo();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
SetLastStatus(hr);
|
||
|
hr = MapHrToUmiError(hr);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::Commit --- IUmiObject support.
|
||
|
//
|
||
|
// Synopsis: Implements IUmiObject::Commit. Calls SetInfo on WinNT
|
||
|
// object to commit changes made to the cache.
|
||
|
//
|
||
|
// Arguments: uFlags - Only 0 for now.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise
|
||
|
//
|
||
|
// Modifies: N/A.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::Commit(ULONG uFlags)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
IADsObjOptPrivate *pPrivOpt = NULL;
|
||
|
DWORD dwFlags;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
if (uFlags > UMI_DONT_COMMIT_SECURITY_DESCRIPTOR) {
|
||
|
SetLastStatus(UMI_E_INVALID_FLAGS);
|
||
|
RRETURN(UMI_E_INVALID_FLAGS);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If this is set do not commit security descriptor.
|
||
|
//
|
||
|
if (uFlags & UMI_DONT_COMMIT_SECURITY_DESCRIPTOR) {
|
||
|
//
|
||
|
// The prop manager has to do this cause it has the prop cache.
|
||
|
//
|
||
|
hr = _pPropMgr->DeleteSDIfPresent();
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
}
|
||
|
|
||
|
if (uFlags & UMI_SECURITY_MASK) {
|
||
|
//
|
||
|
// Need to make sure that we update the SD
|
||
|
// flags on the ADSI object if necessary.
|
||
|
//
|
||
|
if (!_pIADs) {
|
||
|
BAIL_ON_FAILURE(hr = E_FAIL);
|
||
|
}
|
||
|
|
||
|
hr = _pIADs->QueryInterface(
|
||
|
IID_IADsObjOptPrivate,
|
||
|
(void **)&pPrivOpt
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
dwFlags = uFlags & UMI_SECURITY_MASK;
|
||
|
|
||
|
hr = pPrivOpt->SetOption(
|
||
|
LDAP_SECURITY_MASK,
|
||
|
(void *) &dwFlags
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
}
|
||
|
|
||
|
hr = _pIADs->SetInfo();
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
SetLastStatus(hr);
|
||
|
hr = MapHrToUmiError(hr);
|
||
|
}
|
||
|
|
||
|
if (pPrivOpt) {
|
||
|
pPrivOpt->Release();
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// IUmiPropList methods
|
||
|
//
|
||
|
// These are implemented by invoking the corresponding method in the
|
||
|
// CUmiPropList object that implements object properties. For a description
|
||
|
// of these methods, refer to cpropmgr.cxx
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::Put(
|
||
|
LPCWSTR pszName,
|
||
|
ULONG uFlags,
|
||
|
UMI_PROPERTY_VALUES *pProp
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
ULONG ulStatus = 0;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
if (!_pPropMgr) {
|
||
|
SetLastStatus(E_NOTIMPL);
|
||
|
RRETURN(E_NOTIMPL);
|
||
|
}
|
||
|
|
||
|
hr = _pPropMgr->Put(
|
||
|
pszName,
|
||
|
uFlags,
|
||
|
pProp
|
||
|
);
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
IID iid;
|
||
|
//
|
||
|
// Need to update the error status on this object.
|
||
|
//
|
||
|
_pPropMgr->GetLastStatus( // ignore error return
|
||
|
0,
|
||
|
&ulStatus,
|
||
|
iid,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
SetLastStatus(ulStatus);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::Get(
|
||
|
LPCWSTR pszName,
|
||
|
ULONG uFlags,
|
||
|
UMI_PROPERTY_VALUES **ppProp
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
ULONG ulStatus = 0;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
if (!_pPropMgr) {
|
||
|
SetLastStatus(E_NOTIMPL);
|
||
|
RRETURN(E_NOTIMPL);
|
||
|
}
|
||
|
|
||
|
hr = _pPropMgr->Get(
|
||
|
pszName,
|
||
|
uFlags,
|
||
|
ppProp
|
||
|
);
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
//
|
||
|
// Update error on this object appropriately.
|
||
|
//
|
||
|
IID iid;
|
||
|
_pPropMgr->GetLastStatus(
|
||
|
0,
|
||
|
&ulStatus,
|
||
|
iid,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
SetLastStatus(ulStatus);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// No need to map the error as the property manager would have
|
||
|
// already done that for us.
|
||
|
//
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::GetAs(
|
||
|
LPCWSTR pszName,
|
||
|
ULONG uFlags,
|
||
|
ULONG uCoercionType,
|
||
|
UMI_PROPERTY_VALUES **ppProp
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
ULONG ulStatus = 0;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
if (!_pPropMgr) {
|
||
|
SetLastStatus(E_NOTIMPL);
|
||
|
RRETURN(E_NOTIMPL);
|
||
|
}
|
||
|
|
||
|
hr = _pPropMgr->GetAs(
|
||
|
pszName,
|
||
|
uFlags,
|
||
|
uCoercionType,
|
||
|
ppProp
|
||
|
);
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
//
|
||
|
// Update error on this object appropriately.
|
||
|
//
|
||
|
IID iid;
|
||
|
_pPropMgr->GetLastStatus( // ignore error return
|
||
|
0,
|
||
|
&ulStatus,
|
||
|
iid,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
SetLastStatus(ulStatus);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// No need to map hr as property manager would have already done that.
|
||
|
//
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::FreeMemory(
|
||
|
ULONG uReserved,
|
||
|
LPVOID pMem
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
if (uReserved) {
|
||
|
SetLastStatus(E_INVALIDARG);
|
||
|
RRETURN(E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
hr = FreeUmiPropertyValues((PUMI_PROPERTY_VALUES) pMem);
|
||
|
|
||
|
SetLastStatus(hr);
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::GetAt(
|
||
|
LPCWSTR pszName,
|
||
|
ULONG uFlags,
|
||
|
ULONG uBufferLength,
|
||
|
LPVOID pExistingMem
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
ULONG ulStatus = 0;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
if (!_pPropMgr) {
|
||
|
SetLastStatus(E_NOTIMPL);
|
||
|
RRETURN(E_NOTIMPL);
|
||
|
}
|
||
|
|
||
|
hr = _pPropMgr->GetAt(
|
||
|
pszName,
|
||
|
uFlags,
|
||
|
uBufferLength,
|
||
|
pExistingMem
|
||
|
);
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
//
|
||
|
// Update error on this object appropriately.
|
||
|
//
|
||
|
IID iid;
|
||
|
_pPropMgr->GetLastStatus( // ignore error return
|
||
|
0,
|
||
|
&ulStatus,
|
||
|
iid,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
SetLastStatus(ulStatus);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::GetProps(
|
||
|
LPCWSTR *pszNames,
|
||
|
ULONG uNameCount,
|
||
|
ULONG uFlags,
|
||
|
UMI_PROPERTY_VALUES **pProps
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
ULONG ulStatus = 0;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
if (!_pPropMgr) {
|
||
|
SetLastStatus(E_NOTIMPL);
|
||
|
RRETURN(E_NOTIMPL);
|
||
|
}
|
||
|
|
||
|
hr = _pPropMgr->GetProps(
|
||
|
pszNames,
|
||
|
uNameCount,
|
||
|
uFlags,
|
||
|
pProps
|
||
|
);
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
//
|
||
|
// Update error on this object appropriately.
|
||
|
//
|
||
|
IID iid;
|
||
|
_pPropMgr->GetLastStatus( // ignore error return
|
||
|
0,
|
||
|
&ulStatus,
|
||
|
iid,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
SetLastStatus(ulStatus);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::PutProps(
|
||
|
LPCWSTR *pszNames,
|
||
|
ULONG uNameCount,
|
||
|
ULONG uFlags,
|
||
|
UMI_PROPERTY_VALUES *pProps
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
ULONG ulStatus = 0;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
if (!_pPropMgr) {
|
||
|
SetLastStatus(E_NOTIMPL);
|
||
|
RRETURN(E_NOTIMPL);
|
||
|
}
|
||
|
|
||
|
hr = _pPropMgr->PutProps(
|
||
|
pszNames,
|
||
|
uNameCount,
|
||
|
uFlags,
|
||
|
pProps
|
||
|
);
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
//
|
||
|
// Update error on this object appropriately.
|
||
|
//
|
||
|
IID iid;
|
||
|
_pPropMgr->GetLastStatus(
|
||
|
0,
|
||
|
&ulStatus,
|
||
|
iid,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
SetLastStatus(ulStatus);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
HRESULT CLDAPUmiObject::PutFrom(
|
||
|
LPCWSTR pszName,
|
||
|
ULONG uFlags,
|
||
|
ULONG uBufferLength,
|
||
|
LPVOID pExistingMem
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
ULONG ulStatus = 0;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
if (!_pPropMgr) {
|
||
|
SetLastStatus(E_NOTIMPL);
|
||
|
RRETURN(E_NOTIMPL);
|
||
|
}
|
||
|
|
||
|
hr = _pPropMgr->PutFrom(
|
||
|
pszName,
|
||
|
uFlags,
|
||
|
uBufferLength,
|
||
|
pExistingMem
|
||
|
);
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
//
|
||
|
// Update error on this object appropriately.
|
||
|
//
|
||
|
IID iid;
|
||
|
|
||
|
_pPropMgr->GetLastStatus(
|
||
|
0,
|
||
|
&ulStatus,
|
||
|
iid,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
SetLastStatus(ulStatus);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::Delete(
|
||
|
LPCWSTR pszName,
|
||
|
ULONG uFlags
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
ULONG ulStatus = 0;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
if (!_pPropMgr) {
|
||
|
SetLastStatus(E_NOTIMPL);
|
||
|
RRETURN(E_NOTIMPL);
|
||
|
}
|
||
|
|
||
|
hr = _pPropMgr->Delete(
|
||
|
pszName,
|
||
|
uFlags
|
||
|
);
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
//
|
||
|
// Update error on this object appropriately.
|
||
|
//
|
||
|
IID iid;
|
||
|
_pPropMgr->GetLastStatus( // ignore error return
|
||
|
0,
|
||
|
&ulStatus,
|
||
|
iid,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
SetLastStatus(ulStatus);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::GetLastStatus --- IUmiBaseObject support.
|
||
|
//
|
||
|
// Synopsis: Returns status or error code from the last operation. Currently
|
||
|
// only numeric status is returned i.e, no error objects are
|
||
|
// returned. Implements IUmiBaseObject::GetLastStatus().
|
||
|
//
|
||
|
// Arguments: uFlags - Reserved. Must be 0 for now.
|
||
|
// puSpecificStatus - Returns status code.
|
||
|
// riid - IID requested. Ignored currently.
|
||
|
// pStatusObj - Returns interface requested.
|
||
|
// Always returns NULL currently.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: *puSpecificStatus to return status code.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::GetLastStatus(
|
||
|
ULONG uFlags,
|
||
|
ULONG *puSpecificStatus,
|
||
|
REFIID riid,
|
||
|
LPVOID *pStatusObj
|
||
|
)
|
||
|
{
|
||
|
if (pStatusObj) {
|
||
|
*pStatusObj = NULL;
|
||
|
}
|
||
|
|
||
|
if (puSpecificStatus) {
|
||
|
*puSpecificStatus = 0;
|
||
|
}
|
||
|
else {
|
||
|
RRETURN(E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
if (uFlags) {
|
||
|
RRETURN(UMI_E_INVALID_FLAGS);
|
||
|
}
|
||
|
|
||
|
*puSpecificStatus = _ulErrorStatus;
|
||
|
|
||
|
RRETURN(S_OK);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::GetInterfacePropList --- IUmiBaseObject method.
|
||
|
//
|
||
|
// Synopsis: Returns a pointer to the interface property list implementation
|
||
|
// for the connection object. Implements
|
||
|
// IUmiBaseObject::GetInterfacePropList().
|
||
|
//
|
||
|
// Arguments: uFlags - Reserved. Must be 0 for now.
|
||
|
// pPropList - Returns pointer to IUmiPropertyList interface
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: *pPropList to return interface pointer
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::GetInterfacePropList(
|
||
|
ULONG uFlags,
|
||
|
IUmiPropList **pPropList
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
if (uFlags) {
|
||
|
BAIL_ON_FAILURE(hr = UMI_E_INVALID_FLAGS);
|
||
|
}
|
||
|
|
||
|
if (!pPropList) {
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
ADsAssert(_pIntfPropMgr);
|
||
|
|
||
|
//
|
||
|
// The refCounts are tricky here. When this operation is done,
|
||
|
// the ref on this object goes up by one, so you will need to
|
||
|
// releaserefs on the proplist in order to delete this object.
|
||
|
// This is to prevent the case of a proplist existing without
|
||
|
// the underlying object (ok for WinNT not for LDAP).
|
||
|
//
|
||
|
hr = _pIntfPropMgr->QueryInterface(IID_IUmiPropList, (void **) pPropList);
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
SetLastStatus(hr);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::SetLastStatus --- Internal helper routine.
|
||
|
//
|
||
|
// Synopsis: Sets the status of the last operation. If the status is one
|
||
|
// of the pre-defined error codes, then the status is just set to
|
||
|
// 0 since we are not adding any value by returning the same
|
||
|
// status as the error code.
|
||
|
//
|
||
|
// Arguments: ulStatus - Status to be set
|
||
|
//
|
||
|
// Returns: N/A.
|
||
|
//
|
||
|
// Modifies: ulStatus member variable.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
void
|
||
|
CLDAPUmiObject::SetLastStatus(ULONG ulStatus)
|
||
|
{
|
||
|
_ulErrorStatus = ulStatus;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::Open --- IUmiContainer support.
|
||
|
//
|
||
|
// Synopsis: Opens the object specified by a URL. URL may be native LDAP
|
||
|
// path or any UMI path.
|
||
|
//
|
||
|
// Arguments: pURL - Pointer to an IUmiURL interface
|
||
|
// uFlags - Reserved. Must be 0 for now.
|
||
|
// TargetIID - Interface requested.
|
||
|
// ppInterface - Returns pointer to interface requested.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: *ppInterface to return interface pointer
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::Open(
|
||
|
IUmiURL *pURL,
|
||
|
ULONG uFlags,
|
||
|
REFIID TargetIID,
|
||
|
LPVOID *ppInterface
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
LONG lGenus = UMI_GENUS_INSTANCE;
|
||
|
LPWSTR pszDN = NULL, pszClass = NULL;
|
||
|
IDispatch *pDispObj = NULL;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
//
|
||
|
// Get the class name and the dn from the url.
|
||
|
//
|
||
|
if (!pURL || !ppInterface) {
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
if (uFlags != 0) {
|
||
|
BAIL_ON_FAILURE(hr = UMI_E_INVALID_FLAGS);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Helper will split the url txt to useful pieces.
|
||
|
//
|
||
|
hr = UrlToClassAndDn(pURL, &pszClass, &pszDN);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
//
|
||
|
// At this point if this is a class instance == schema class object,
|
||
|
// then if the dn is of the format name="something", we should just
|
||
|
// pass in "something" to the IADsContainer::Open call.
|
||
|
//
|
||
|
if (pszClass
|
||
|
&& *pszClass
|
||
|
&& !_wcsicmp(pszClass, L"Class")) {
|
||
|
//
|
||
|
// Want to see if this is a class instance.
|
||
|
//
|
||
|
if (_pIntfPropMgr) {
|
||
|
hr = _pIntfPropMgr->GetLongProperty(L"__GENUS", &lGenus);
|
||
|
if (FAILED(hr)) {
|
||
|
//
|
||
|
// We will assume that this is not a class in this case.
|
||
|
//
|
||
|
hr = S_OK;
|
||
|
lGenus = UMI_GENUS_INSTANCE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pszDN // should always be true
|
||
|
&& lGenus == UMI_GENUS_CLASS
|
||
|
) {
|
||
|
if (!_wcsnicmp(pszDN, L"name=", 5)) {
|
||
|
//
|
||
|
// Copy over the new name and replace pszDN with
|
||
|
// the new name.
|
||
|
//
|
||
|
LPWSTR pszReplace;
|
||
|
pszReplace = AllocADsStr(pszDN + 5);
|
||
|
|
||
|
if (!pszReplace) {
|
||
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
||
|
}
|
||
|
|
||
|
if (pszDN) {
|
||
|
FreeADsStr(pszDN);
|
||
|
}
|
||
|
|
||
|
pszDN = pszReplace;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Call the IADSContaienr::GetObject to do the remaining work.
|
||
|
//
|
||
|
hr = _pIADsContainer->GetObject(pszClass, pszDN, &pDispObj);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
if (pszClass && *pszClass) {
|
||
|
//
|
||
|
// We need to compare the name cause, the GetObject code cannot
|
||
|
// do that for umi calls.
|
||
|
//
|
||
|
hr = VerifyIfClassMatches(
|
||
|
pszClass,
|
||
|
pDispObj,
|
||
|
lGenus
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
}
|
||
|
|
||
|
hr = pDispObj->QueryInterface(TargetIID, ppInterface);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (pDispObj) {
|
||
|
pDispObj->Release();
|
||
|
}
|
||
|
|
||
|
if (pszClass) {
|
||
|
FreeADsStr(pszClass);
|
||
|
}
|
||
|
|
||
|
if (pszDN) {
|
||
|
FreeADsStr(pszDN);
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
SetLastStatus(hr);
|
||
|
hr = MapHrToUmiError(hr);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::Put --- IUmiContainer support.
|
||
|
//
|
||
|
// Synopsis: Commits an object into the container. Not implemented.
|
||
|
//
|
||
|
// Arguments: uFlags - Reserved. Must be 0 for now.
|
||
|
// TargetIID - IID of interface pointer requesed.
|
||
|
// pInterface - Output interface pointer.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: *ppInterface to return interface pointer
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::PutObject(
|
||
|
ULONG uFlags,
|
||
|
REFIID TargetIID,
|
||
|
LPVOID pInterface
|
||
|
)
|
||
|
{
|
||
|
SetLastStatus(E_NOTIMPL);
|
||
|
|
||
|
RRETURN(E_NOTIMPL);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::DeleteObject --- IUmiContainer support.
|
||
|
//
|
||
|
// Synopsis: Deletes the object specified by the URL. This calls the
|
||
|
// underlying container object to do the delete after preparing
|
||
|
// the arguments suitably. Note that if no class name is specified
|
||
|
// we will pass in NULL to the IADsContainer::Delete call.
|
||
|
//
|
||
|
// Arguments: pURL - Pointer to URL of object to delete (relative).
|
||
|
// uFlags - Reserved. Must be 0 for now.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: N/A.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::DeleteObject(
|
||
|
IUmiURL *pURL,
|
||
|
ULONG uFlags
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
LPWSTR pszClass = NULL, pszDN = NULL;
|
||
|
|
||
|
if (!pURL) {
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
if (uFlags != 0) {
|
||
|
BAIL_ON_FAILURE(hr = UMI_E_INVALID_FLAGS);
|
||
|
}
|
||
|
|
||
|
hr = UrlToClassAndDn(pURL, &pszClass, &pszDN);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
//
|
||
|
// Call the IADsContainer::Delete entry point to do the work.
|
||
|
//
|
||
|
hr = _pIADsContainer->Delete(pszClass, pszDN);
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (pszClass) {
|
||
|
FreeADsStr(pszClass);
|
||
|
}
|
||
|
|
||
|
if (pszDN) {
|
||
|
FreeADsStr(pszDN);
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
SetLastStatus(hr);
|
||
|
hr = MapHrToUmiError(hr);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::Create --- IUmiContainer support.
|
||
|
//
|
||
|
// Synopsis: Creates the object specified by the URL. There always has
|
||
|
// to be a className for this operation to succeed. The newly
|
||
|
// created object is not yet on the directory, just local (need
|
||
|
// to call Commit to create it on the directory).
|
||
|
//
|
||
|
// Arguments: pURL - Pointer to URL of new object (relative).
|
||
|
// uFlags - Reserved. Must be 0 for now.
|
||
|
// ppNewObj - Return ptr for newly created object.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: *pNewObject to return the IUmiObject interface
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::Create(
|
||
|
IUmiURL *pURL,
|
||
|
ULONG uFlags,
|
||
|
IUmiObject **ppNewObj
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
LPWSTR pszClass = NULL, pszDN = NULL;
|
||
|
IDispatch *pDispObj = NULL;
|
||
|
|
||
|
if (!pURL) {
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
if (uFlags != 0) {
|
||
|
BAIL_ON_FAILURE(hr = UMI_E_INVALID_FLAGS);
|
||
|
}
|
||
|
|
||
|
hr = UrlToClassAndDn(pURL, &pszClass, &pszDN);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
hr = _pIADsContainer->Create(pszClass, pszDN, &pDispObj);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
hr = ((IUnknown *)pDispObj)->QueryInterface(
|
||
|
IID_IUmiObject,
|
||
|
(void **) ppNewObj
|
||
|
);
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (pDispObj) {
|
||
|
pDispObj->Release();
|
||
|
}
|
||
|
|
||
|
if (pszClass) {
|
||
|
FreeADsStr(pszClass);
|
||
|
}
|
||
|
|
||
|
if (pszDN) {
|
||
|
FreeADsStr(pszDN);
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
SetLastStatus(hr);
|
||
|
hr = MapHrToUmiError(hr);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::Move --- IUmiContainer support.
|
||
|
//
|
||
|
// Synopsis: Moves a specified object into the container.
|
||
|
//
|
||
|
// Arguments: uFlags - Reserved. Must be 0 for now.
|
||
|
// pOldURL - URL of the object to be moved.
|
||
|
// pNewURL - New URL of the object within the container.
|
||
|
// If NULL, then no name change is needed.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: N/A.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::Move(
|
||
|
ULONG uFlags,
|
||
|
IUmiURL *pOldURL,
|
||
|
IUmiURL *pNewURL
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
LPWSTR pszOldPath = NULL, pszNewPath = NULL, pszClass = NULL;
|
||
|
ULONGLONG ullPathType = 0;
|
||
|
IDispatch *pDispObj = NULL;
|
||
|
|
||
|
if (!_pIADsContainer) {
|
||
|
BAIL_ON_FAILURE(hr = E_NOTIMPL);
|
||
|
}
|
||
|
|
||
|
if (!pOldURL) {
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// What flags should we support here ?
|
||
|
//
|
||
|
if (uFlags) {
|
||
|
BAIL_ON_FAILURE(hr = UMI_E_INVALID_FLAGS);
|
||
|
}
|
||
|
|
||
|
hr = UrlToLDAPPath(
|
||
|
pOldURL,
|
||
|
&pszOldPath
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
if (pNewURL) {
|
||
|
//
|
||
|
// Update code based on the path type.
|
||
|
//
|
||
|
hr = pNewURL->GetPathInfo(0, &ullPathType);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
if (ullPathType == UMIPATH_INFO_RELATIVE_PATH) {
|
||
|
hr = UrlToClassAndDn(
|
||
|
pNewURL,
|
||
|
&pszClass,
|
||
|
&pszNewPath
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
}
|
||
|
else {
|
||
|
//
|
||
|
// Does this even make sense on a move ???
|
||
|
//
|
||
|
hr = UrlToLDAPPath(
|
||
|
pNewURL,
|
||
|
&pszNewPath
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// At this point both the new and old path will be set correctly.
|
||
|
//
|
||
|
hr = _pIADsContainer->MoveHere(
|
||
|
pszOldPath,
|
||
|
pszNewPath,
|
||
|
&pDispObj
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (pszOldPath) {
|
||
|
FreeADsStr(pszOldPath);
|
||
|
}
|
||
|
|
||
|
if (pszNewPath) {
|
||
|
FreeADsStr(pszNewPath);
|
||
|
}
|
||
|
|
||
|
if (pszClass) {
|
||
|
FreeADsStr(pszClass);
|
||
|
}
|
||
|
|
||
|
if (pDispObj) {
|
||
|
pDispObj->Release();
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
SetLastStatus(hr);
|
||
|
hr = MapHrToUmiError(hr);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::CreateEnum --- IUmiContainer support.
|
||
|
//
|
||
|
// Synopsis: Creates an enumerator within a container. The enumerator is
|
||
|
// an IUmiCursor interface pointer. The caller can optionally set
|
||
|
// a filter on the cursor and then enumerate the contents of the
|
||
|
// container. The actual enumeration of the container does
|
||
|
// not happen in this function. It is deferred to the point
|
||
|
// when the cursor is used to enumerate the results.
|
||
|
//
|
||
|
// Arguments: pszEnumContext - Not used. Must be NULL.
|
||
|
// uFlags - Reserved. Must be 0 for now.
|
||
|
// TargetIID - Interface requested on enum has to be
|
||
|
// IUmiCursor for now.
|
||
|
// ppInterface - Return value for new enurator.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: *ppInterface to return the IUmiCursor interface
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::CreateEnum(
|
||
|
IUmiURL *pszEnumContext,
|
||
|
ULONG uFlags,
|
||
|
REFIID TargetIID,
|
||
|
LPVOID *ppInterface
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
//
|
||
|
// Validate args.
|
||
|
//
|
||
|
if (pszEnumContext || uFlags) {
|
||
|
//
|
||
|
// We do not support contexts currently.
|
||
|
//
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
if (!(TargetIID == IID_IUmiCursor)) {
|
||
|
//
|
||
|
// Type of cursor we do not support.
|
||
|
//
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
hr = CUmiCursor::CreateCursor(
|
||
|
_pIADsContainer,
|
||
|
TargetIID,
|
||
|
ppInterface
|
||
|
);
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
SetLastStatus(hr);
|
||
|
hr = MapHrToUmiError(hr);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::ExecQuery --- IUmiContainer support.
|
||
|
//
|
||
|
// Synopsis: Executes a query on this container.
|
||
|
//
|
||
|
// Arguments: pQuery - Pointer to the query to execute.
|
||
|
// uFlags - Reserved. Must be 0 for now.
|
||
|
// TargetIID - Interface requested.
|
||
|
// ppInterface - Return value for the cursor requested.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: *ppInterface to return interface pointer
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::ExecQuery(
|
||
|
IUmiQuery *pQuery,
|
||
|
ULONG uFlags,
|
||
|
REFIID TargetIID,
|
||
|
LPVOID *ppResult
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
CLDAPConObject *pConnection = NULL;
|
||
|
BSTR bstrADsPath = NULL;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
if (!ppResult
|
||
|
|| !pQuery
|
||
|
|| !(TargetIID == IID_IUmiCursor)
|
||
|
) {
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
*ppResult = NULL;
|
||
|
|
||
|
if (uFlags != 0) {
|
||
|
BAIL_ON_FAILURE(hr = UMI_E_INVALID_FLAGS);
|
||
|
}
|
||
|
|
||
|
hr = this->_pIADs->get_ADsPath(&bstrADsPath);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
//
|
||
|
// We want to pre-process this query and verify that it is
|
||
|
// valid if this is a SQL/WQL style query. This is a little
|
||
|
// bit of double effort but provides a much easier usage
|
||
|
// paradigm.
|
||
|
//
|
||
|
hr = VerifyIfQueryIsValid(pQuery);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
hr = CLDAPConObject::CreateConnectionObject(
|
||
|
&pConnection,
|
||
|
this->_pLdapHandle
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
hr = CUmiCursor::CreateCursor(
|
||
|
pQuery,
|
||
|
pConnection,
|
||
|
(IUmiObject *) this,
|
||
|
bstrADsPath,
|
||
|
_pszLDAPServer,
|
||
|
_pszLDAPDn,
|
||
|
*_pCreds,
|
||
|
_dwPort,
|
||
|
TargetIID,
|
||
|
ppResult
|
||
|
);
|
||
|
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
|
||
|
error :
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
if (*ppResult) {
|
||
|
((IUnknown*)(*ppResult))->Release();
|
||
|
}
|
||
|
SetLastStatus(hr);
|
||
|
hr = MapHrToUmiError(hr);
|
||
|
}
|
||
|
|
||
|
if (bstrADsPath) {
|
||
|
SysFreeString(bstrADsPath);
|
||
|
}
|
||
|
|
||
|
if (pConnection) {
|
||
|
pConnection->Release();
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CLDAPUmiObject::GetCLSIDForIID --- ICustomInterfaceFactory.
|
||
|
//
|
||
|
// Synopsis: Returns the CLSID corresponding to a given interface IID. If
|
||
|
// the interface is one of the interfaces implemented by the
|
||
|
// underlying LDAP object, then CLSID_LDAPObject is returned.
|
||
|
// If the IID is one of the interfaces implemented by an
|
||
|
// extension object, then the extension's CLSID is returned.
|
||
|
//
|
||
|
// Arguments: riid - Interface ID for which we want to find the CLSID.
|
||
|
// lFlags - Reserved. Must be 0.
|
||
|
// pCLSID - Returns the CLSID corresponding to the IID.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: *pCLSID to return CLSID.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::GetCLSIDForIID(
|
||
|
REFIID riid,
|
||
|
long lFlags,
|
||
|
CLSID *pCLSID
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
IUnknown *pUnknown = NULL;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
if ( (lFlags) || (!pCLSID) ) {
|
||
|
SetLastStatus(E_INVALIDARG);
|
||
|
RRETURN(E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
if (_pExtMgr) {
|
||
|
//
|
||
|
// Check if there is any extension that supports this IID.
|
||
|
//
|
||
|
hr = _pExtMgr->GetCLSIDForIID(
|
||
|
riid,
|
||
|
lFlags,
|
||
|
pCLSID
|
||
|
);
|
||
|
|
||
|
if (SUCCEEDED(hr)) {
|
||
|
RRETURN(S_OK);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// check if the underlying LDAP object supports this IID
|
||
|
//
|
||
|
hr = _pUnkInner->QueryInterface(riid, (void **) &pUnknown);
|
||
|
|
||
|
if (SUCCEEDED(hr)) {
|
||
|
pUnknown->Release();
|
||
|
|
||
|
memcpy(pCLSID, &CLSID_LDAPObject, sizeof(GUID));
|
||
|
|
||
|
RRETURN(S_OK);
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
SetLastStatus(hr);
|
||
|
hr = MapHrToUmiError(hr);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: GetObjectByCLSID --- IUmiCustomInterfaceFactory support.
|
||
|
//
|
||
|
// Synopsis: Returns a pointer to a requested interface on the object
|
||
|
// specified by a CLSID. The object specified by the CLSID is
|
||
|
// aggregated by the specified outer unknown. The interface
|
||
|
// returned is a non-delegating interface on the object.
|
||
|
//
|
||
|
// Arguments: clsid - CLSID of object supporting requested interface.
|
||
|
// pUnkOuter - Aggregating outer unknown.
|
||
|
// dwClsContext - Context for running executable code.
|
||
|
// riid - Interface requested.
|
||
|
// lFlags - Reserved. Must be 0.
|
||
|
// ppInterface - Returns requested interface.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: *ppInterface to return requested interface
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::GetObjectByCLSID(
|
||
|
CLSID clsid,
|
||
|
IUnknown *pUnkOuter,
|
||
|
DWORD dwClsContext,
|
||
|
REFIID riid,
|
||
|
long lFlags,
|
||
|
void **ppInterface
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
IUnknown *pCurOuterUnk = NULL;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
if ( (lFlags != 0)
|
||
|
|| (!pUnkOuter)
|
||
|
|| (!ppInterface)
|
||
|
|| (dwClsContext != CLSCTX_INPROC_SERVER) )
|
||
|
{
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// ensure outer unknown specified is same as what is on the LDAP object
|
||
|
//
|
||
|
if (_fOuterUnkSet) {
|
||
|
pCurOuterUnk = _pCoreObj->GetOuterUnknown();
|
||
|
|
||
|
if (pCurOuterUnk != pUnkOuter) {
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The interface requested has to be IUnknown if there is
|
||
|
// an outer uknown ptr.
|
||
|
//
|
||
|
if (!IsEqualIID(riid, IID_IUnknown)) {
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!IsEqualCLSID(clsid, CLSID_LDAPObject)) {
|
||
|
//
|
||
|
// has to be a CLSID of an extension object
|
||
|
//
|
||
|
if (_pExtMgr) {
|
||
|
|
||
|
hr = _pExtMgr->GetObjectByCLSID(
|
||
|
clsid,
|
||
|
pUnkOuter,
|
||
|
riid,
|
||
|
ppInterface
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
//
|
||
|
// successfully got the interface
|
||
|
//
|
||
|
_pCoreObj->SetOuterUnknown(pUnkOuter);
|
||
|
_fOuterUnkSet = TRUE;
|
||
|
|
||
|
RRETURN(S_OK);
|
||
|
}
|
||
|
else {
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG); // bad CLSID
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// CLSID == CLSID_LDAPObject. This has to be an interface on the
|
||
|
// underlying LDAP object. Check if the LDAP object supports this IID.
|
||
|
//
|
||
|
hr = _pUnkInner->QueryInterface(riid, ppInterface);
|
||
|
|
||
|
if (SUCCEEDED(hr)) {
|
||
|
//
|
||
|
// successfully got the interface
|
||
|
//
|
||
|
_pCoreObj->SetOuterUnknown(pUnkOuter);
|
||
|
_fOuterUnkSet = TRUE;
|
||
|
|
||
|
RRETURN(S_OK);
|
||
|
}
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
SetLastStatus(hr);
|
||
|
hr = MapHrToUmiError(hr);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: GetCLSIDForNames --- ICustomInterfaceFactory support.
|
||
|
//
|
||
|
// Synopsis: Returns the CLSID of the object that supports a specified
|
||
|
// method/property. Also returns DISPIDs for the property/method.
|
||
|
//
|
||
|
// Arguments: rgszNames - Names to be mapped.
|
||
|
// cNames - Number of names to be mapped.
|
||
|
// lcid - Locale in which to interpret the names.
|
||
|
// rgDispId - Returns DISPID.
|
||
|
// lFlags - Reserved. Must be 0.
|
||
|
// pCLSID - Returns CLSID of object supporting this
|
||
|
// property/method.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: *pCLSID to return the CLSID.
|
||
|
// *rgDispId to return the DISPIDs.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::GetCLSIDForNames(
|
||
|
LPOLESTR *rgszNames,
|
||
|
UINT cNames,
|
||
|
LCID lcid,
|
||
|
DISPID *rgDispId,
|
||
|
long lFlags,
|
||
|
CLSID *pCLSID
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
IDispatch *pDispatch = NULL;
|
||
|
|
||
|
SetLastStatus(0);
|
||
|
|
||
|
if ( (lFlags != 0) || (!pCLSID) ) {
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
if (cNames == 0) {
|
||
|
RRETURN(S_OK);
|
||
|
}
|
||
|
|
||
|
if ( (!rgszNames) || (!rgDispId) ) {
|
||
|
RRETURN(S_OK);
|
||
|
}
|
||
|
|
||
|
if (_pExtMgr) {
|
||
|
//
|
||
|
// check if there is any extension which supports this IID
|
||
|
//
|
||
|
hr = _pExtMgr->GetCLSIDForNames(
|
||
|
rgszNames,
|
||
|
cNames,
|
||
|
lcid,
|
||
|
rgDispId,
|
||
|
lFlags,
|
||
|
pCLSID
|
||
|
);
|
||
|
if (SUCCEEDED(hr)) {
|
||
|
//
|
||
|
// successfully got the CLSID and DISPIDs
|
||
|
//
|
||
|
RRETURN(S_OK);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check if the underlying LDAP object supports this name
|
||
|
//
|
||
|
hr = _pUnkInner->QueryInterface(IID_IDispatch, (void **) &pDispatch);
|
||
|
if (FAILED(hr)) {
|
||
|
BAIL_ON_FAILURE(hr = E_FAIL);
|
||
|
}
|
||
|
|
||
|
hr = pDispatch->GetIDsOfNames(
|
||
|
IID_NULL,
|
||
|
rgszNames,
|
||
|
cNames,
|
||
|
lcid,
|
||
|
rgDispId
|
||
|
);
|
||
|
if (SUCCEEDED(hr)) {
|
||
|
pDispatch->Release();
|
||
|
memcpy(pCLSID, &CLSID_LDAPObject, sizeof(GUID));
|
||
|
|
||
|
RRETURN(S_OK);
|
||
|
}
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (pDispatch) {
|
||
|
pDispatch->Release();
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
SetLastStatus(hr);
|
||
|
hr = MapHrToUmiError(hr);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: GetOption --- IADsObjOptPrivate support.
|
||
|
//
|
||
|
// Synopsis: Private interface for internal use, this function is used to
|
||
|
// return the ldap handle if applicable.
|
||
|
//
|
||
|
// Arguments: dwOption - Option being read.
|
||
|
// pValue - Return pointer to hold value of option.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: pValue to return requested option.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::GetOption(
|
||
|
DWORD dwOption,
|
||
|
void *pValue
|
||
|
)
|
||
|
{
|
||
|
if (dwOption != LDP_CACHE_ENTRY) {
|
||
|
RRETURN(E_FAIL);
|
||
|
}
|
||
|
else {
|
||
|
*((PADSLDP *) pValue) = _pLdapHandle;
|
||
|
RRETURN(S_OK);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: SetOption --- IADsObjOptPrivate support.
|
||
|
//
|
||
|
// Synopsis: Private interface for internal use - NOTIMPL.
|
||
|
//
|
||
|
// Arguments: dwOption - Option being set
|
||
|
// pValue - Value of option being set.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: pValue to return requested option.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
STDMETHODIMP
|
||
|
CLDAPUmiObject::SetOption(
|
||
|
DWORD dwOption,
|
||
|
void *pValue
|
||
|
)
|
||
|
{
|
||
|
RRETURN(E_NOTIMPL);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: CopyToHelper --- Helper routine protected scope.
|
||
|
//
|
||
|
// Synopsis: Copies the attributes on the source object over to the
|
||
|
// destination object.
|
||
|
//
|
||
|
// Arguments: pUmiObjSr --- Source object.
|
||
|
// pUmiObjDest --- Destination object to copy attributes to.
|
||
|
// uFlags --- Only 0 is supported currently.
|
||
|
// fMarkAsUpdate --- Flag indicating if we should mark the
|
||
|
// attributes on dest as update rather than 0.
|
||
|
// fCopyIntfProps --- Only false is supported currently.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code otherwise.
|
||
|
//
|
||
|
// Modifies: pUmiObjDest
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
HRESULT
|
||
|
CLDAPUmiObject::CopyToHelper(
|
||
|
IUmiObject *pUmiSrcObj,
|
||
|
IUmiObject *pUmiDestObj,
|
||
|
ULONG uFlags,
|
||
|
BOOL fMarkAsUpdate, // default is TRUE
|
||
|
BOOL fCopyIntfProps // default is FALSE
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
ULONG ulLastError = S_OK;
|
||
|
PUMI_PROPERTY_VALUES pUmiPropValsList = NULL;
|
||
|
DWORD dwCount;
|
||
|
|
||
|
|
||
|
if (!pUmiSrcObj || !pUmiDestObj || uFlags) {
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We need to get the list of properties from the src object.
|
||
|
//
|
||
|
hr = pUmiSrcObj->GetProps(
|
||
|
NULL,
|
||
|
0,
|
||
|
UMI_FLAG_GETPROPS_NAMES,
|
||
|
&pUmiPropValsList
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
for (dwCount = 0; dwCount < pUmiPropValsList->uCount; dwCount++) {
|
||
|
//
|
||
|
// We need to walk the list, get each properties name and then
|
||
|
// for each property get its values from the source and put them
|
||
|
// on the destination.
|
||
|
//
|
||
|
LPWSTR pszTempStr =
|
||
|
pUmiPropValsList->pPropArray[dwCount].pszPropertyName;
|
||
|
PUMI_PROPERTY_VALUES pCurProp = NULL;
|
||
|
|
||
|
if (!pszTempStr) {
|
||
|
//
|
||
|
// Name should never be NULL.
|
||
|
//
|
||
|
BAIL_ON_FAILURE(hr = E_FAIL);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We need to add a check for the SD cause we want to copy that over
|
||
|
// as a binary blob to prevent unnecessary traffic.
|
||
|
//
|
||
|
|
||
|
// Should change the get to use the force flag once it is done.
|
||
|
//
|
||
|
hr = pUmiSrcObj->Get(
|
||
|
pszTempStr,
|
||
|
0,
|
||
|
&pCurProp
|
||
|
);
|
||
|
if (FAILED(hr)) {
|
||
|
pUmiSrcObj->GetLastStatus(0, &ulLastError, IID_IUnknown, NULL);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
}
|
||
|
|
||
|
hr = pUmiDestObj->Put(
|
||
|
pszTempStr,
|
||
|
0x8000000,
|
||
|
pCurProp
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Irrespective of this operation success/failure we need to free
|
||
|
// the contents of pCurProp.
|
||
|
//
|
||
|
pUmiSrcObj->FreeMemory(0, (void *) pCurProp);
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
pUmiDestObj->GetLastStatus(0, &ulLastError, IID_IUnknown, NULL);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
}
|
||
|
|
||
|
} // for each property in the source object.
|
||
|
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (pUmiPropValsList) {
|
||
|
pUmiSrcObj->FreeMemory(0, (void *) pUmiPropValsList);
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
if (ulLastError) {
|
||
|
SetLastStatus(ulLastError);
|
||
|
}
|
||
|
else {
|
||
|
SetLastStatus(hr);
|
||
|
}
|
||
|
|
||
|
hr = MapHrToUmiError(hr);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: VerifyifQueryIsValid --- Helper routine protected scope.
|
||
|
//
|
||
|
// Synopsis: Copies the attributes on the source object over to the
|
||
|
// destination object.
|
||
|
//
|
||
|
// Arguments: pUmiQeury --- Query object to validate.
|
||
|
//
|
||
|
// Returns: S_OK on success. Error code from parser otherwise.
|
||
|
//
|
||
|
// Modifies: N/A.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
HRESULT
|
||
|
CLDAPUmiObject::VerifyIfQueryIsValid(
|
||
|
IUmiQuery *pUmiQuery
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
ULONG ulLangBufSize = 100 * sizeof(WCHAR);
|
||
|
ULONG ulQueryBufSize = MAX_PATH * sizeof(WCHAR);
|
||
|
WCHAR pszLangBuf[100];
|
||
|
WCHAR szQueryText[MAX_PATH];
|
||
|
LPWSTR pszQueryText = szQueryText;
|
||
|
IWbemQuery *pQueryParser = NULL;
|
||
|
ULONG ulFeatures[] = {
|
||
|
WMIQ_LF1_BASIC_SELECT,
|
||
|
WMIQ_LF2_CLASS_NAME_IN_QUERY,
|
||
|
WMIQ_LF6_ORDER_BY,
|
||
|
WMIQ_LF24_UMI_EXTENSIONS
|
||
|
};
|
||
|
|
||
|
//
|
||
|
// We need to look at the query language if the language is SQL,
|
||
|
// then we need to go through and convert the SQL settings to
|
||
|
// properties on the intfPropList of the query.
|
||
|
//
|
||
|
hr = pUmiQuery->GetQuery(
|
||
|
&ulLangBufSize,
|
||
|
pszLangBuf,
|
||
|
&ulQueryBufSize,
|
||
|
pszQueryText
|
||
|
);
|
||
|
|
||
|
if (hr == E_OUTOFMEMORY ) {
|
||
|
//
|
||
|
// Means there was insufficient length in the buffers.
|
||
|
//
|
||
|
if (ulQueryBufSize > (MAX_PATH * sizeof(WCHAR))) {
|
||
|
pszQueryText = (LPWSTR) AllocADsMem(
|
||
|
ulQueryBufSize + sizeof(WCHAR)
|
||
|
);
|
||
|
if (pszQueryText) {
|
||
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
hr = pUmiQuery->GetQuery(
|
||
|
&ulLangBufSize,
|
||
|
pszLangBuf,
|
||
|
&ulQueryBufSize,
|
||
|
pszQueryText
|
||
|
);
|
||
|
}
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
if (*pszLangBuf) {
|
||
|
if (!_wcsicmp(L"SQL", pszLangBuf)
|
||
|
|| !_wcsicmp(L"WQL", pszLangBuf)
|
||
|
) {
|
||
|
//
|
||
|
// Create the WBEM parser object and verify the query is valid.
|
||
|
//
|
||
|
hr = CoCreateInstance(
|
||
|
CLSID_WbemQuery,
|
||
|
NULL,
|
||
|
CLSCTX_INPROC_SERVER,
|
||
|
IID_IWbemQuery,
|
||
|
(LPVOID *) &pQueryParser
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
//
|
||
|
// Set the query into the parser and try and parse the query.
|
||
|
//
|
||
|
hr = pQueryParser->SetLanguageFeatures(
|
||
|
0,
|
||
|
sizeof(ulFeatures)/sizeof(ULONG),
|
||
|
ulFeatures
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
hr = pQueryParser->Parse(L"SQL", pszQueryText, 0);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
} // if the language is SQL
|
||
|
} // if the language has been set
|
||
|
else {
|
||
|
//
|
||
|
// No language ???.
|
||
|
//
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
SetLastStatus(hr);
|
||
|
//
|
||
|
// For now do not map error as we want error from parser
|
||
|
// until such time as new error codes are added to umi.
|
||
|
// hr = MapHrToUmiError(hr);
|
||
|
}
|
||
|
|
||
|
if (pQueryParser) {
|
||
|
pQueryParser->Release();
|
||
|
}
|
||
|
|
||
|
if (pszQueryText && pszQueryText != szQueryText) {
|
||
|
FreeADsMem(pszQueryText);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Function: VerifyIfClassMatches --- Helper routine protected scope.
|
||
|
//
|
||
|
// Synopsis: Makes sure the class name of the object matches the class
|
||
|
// asked for.
|
||
|
//
|
||
|
// Arguments: pszClass --- Class requested.
|
||
|
// pUnk --- Ptr to IUnk of Umi Object.
|
||
|
// lGenus --- Is the parent a schema object or not ?
|
||
|
//
|
||
|
// Returns: S_OK on success. Any failure error code or E_INVALIDARG.
|
||
|
//
|
||
|
// Modifies: N/A.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
HRESULT
|
||
|
CLDAPUmiObject::VerifyIfClassMatches(
|
||
|
LPWSTR pszClass,
|
||
|
IUnknown * pUnk,
|
||
|
LONG lGenus
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
IUmiObject *pUmiObject = NULL;
|
||
|
IUmiPropList *pPropList = NULL;
|
||
|
UMI_PROPERTY_VALUES *pPropVals = NULL;
|
||
|
|
||
|
//
|
||
|
// For now the schema will always succeed cause we have no
|
||
|
// way to verify the parent class. We will fail the GetObject
|
||
|
// calls on the schema objects with bad paths.
|
||
|
//
|
||
|
if (lGenus == UMI_GENUS_CLASS) {
|
||
|
RRETURN(hr);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get hold of the IUmiObject and then the proplist from it.
|
||
|
//
|
||
|
hr = pUnk->QueryInterface(
|
||
|
IID_IUmiObject,
|
||
|
(void **) &pUmiObject
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
hr = pUmiObject->GetInterfacePropList(
|
||
|
0,
|
||
|
&pPropList
|
||
|
);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
hr = pPropList->Get(L"__CLASS", 0, &pPropVals);
|
||
|
BAIL_ON_FAILURE(hr);
|
||
|
|
||
|
//
|
||
|
// Should have one value that we need to compare
|
||
|
//
|
||
|
if (!pPropVals
|
||
|
|| (pPropVals->uCount != 1)
|
||
|
|| !pPropVals->pPropArray
|
||
|
|| !pPropVals->pPropArray[0].pUmiValue
|
||
|
|| !pPropVals->pPropArray[0].pUmiValue->pszStrValue
|
||
|
|| !pPropVals->pPropArray[0].pUmiValue->pszStrValue[0]
|
||
|
) {
|
||
|
BAIL_ON_FAILURE(hr = E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Failure if the names do not match.
|
||
|
//
|
||
|
if (_wcsicmp(
|
||
|
pPropVals->pPropArray[0].pUmiValue->pszStrValue[0],
|
||
|
pszClass
|
||
|
)
|
||
|
) {
|
||
|
hr = E_INVALIDARG;
|
||
|
}
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (pUmiObject) {
|
||
|
pUmiObject->Release();
|
||
|
}
|
||
|
|
||
|
if (pPropList) {
|
||
|
pPropList->Release();
|
||
|
}
|
||
|
|
||
|
if (pPropVals) {
|
||
|
FreeMemory(0, (void*)pPropVals);
|
||
|
}
|
||
|
|
||
|
RRETURN(hr);
|
||
|
}
|