You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
801 lines
20 KiB
801 lines
20 KiB
//---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1996
|
|
//
|
|
// File: cenumgrp.cxx
|
|
//
|
|
// Contents: LDAP GroupCollection Enumeration Code
|
|
//
|
|
// CLDAPGroupCollectionEnum::
|
|
// CLDAPGroupCollectionEnum::
|
|
// CLDAPGroupCollectionEnum::
|
|
// CLDAPGroupCollectionEnum::
|
|
//
|
|
// History:
|
|
//----------------------------------------------------------------------------
|
|
#include "ldap.hxx"
|
|
#pragma hdrstop
|
|
|
|
HRESULT
|
|
BuildADsPathFromLDAPPath(
|
|
LPWSTR szNamespace,
|
|
LPWSTR szLdapDN,
|
|
LPWSTR * ppszADsPathName
|
|
);
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: LDAPEnumVariant::Create
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments: [pCollection]
|
|
// [ppEnumVariant]
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 01-30-95 krishnag Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
CLDAPGroupCollectionEnum::Create(
|
|
CLDAPGroupCollectionEnum FAR* FAR* ppenumvariant,
|
|
BSTR Parent,
|
|
BSTR ADsPath,
|
|
BSTR GroupName,
|
|
VARIANT vMembers,
|
|
VARIANT vFilter,
|
|
CCredentials& Credentials,
|
|
IDirectoryObject * pIDirObj,
|
|
BOOL fRangeRetrieval
|
|
)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
CLDAPGroupCollectionEnum FAR* penumvariant = NULL;
|
|
long lLBound = 0;
|
|
long lUBound = 0;
|
|
|
|
*ppenumvariant = NULL;
|
|
|
|
penumvariant = new CLDAPGroupCollectionEnum();
|
|
if (!penumvariant) {
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
penumvariant->_fRangeRetrieval = fRangeRetrieval;
|
|
|
|
hr = ADsAllocString( Parent , &penumvariant->_Parent);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
hr = ADsAllocString(GroupName, &penumvariant->_GroupName);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
hr = ADsAllocString(ADsPath, &penumvariant->_ADsPath);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
hr = VariantCopy( &penumvariant->_vMembers, &vMembers );
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if ( vMembers.vt == VT_BSTR ) // 1 member only
|
|
{
|
|
penumvariant->_lMembersCount = 1;
|
|
}
|
|
else
|
|
{
|
|
hr = SafeArrayGetLBound(V_ARRAY(&penumvariant->_vMembers),
|
|
1,
|
|
(long FAR *)&lLBound
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
hr = SafeArrayGetUBound(V_ARRAY(&penumvariant->_vMembers),
|
|
1,
|
|
(long FAR *)&lUBound
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
penumvariant->_lMembersCount = lUBound - lLBound + 1;
|
|
}
|
|
|
|
hr = ObjectTypeList::CreateObjectTypeList(
|
|
vFilter,
|
|
&penumvariant->_pObjList
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
penumvariant->_Credentials = Credentials;
|
|
|
|
pIDirObj->QueryInterface(
|
|
IID_IDirectoryObject,
|
|
(void **) &(penumvariant->_pIDirObj)
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
*ppenumvariant = penumvariant;
|
|
|
|
RRETURN(hr);
|
|
|
|
error:
|
|
delete penumvariant;
|
|
|
|
RRETURN_EXP_IF_ERR(hr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Function:
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
CLDAPGroupCollectionEnum::CLDAPGroupCollectionEnum()
|
|
: _Parent(NULL),
|
|
_ADsPath(NULL),
|
|
_GroupName(NULL),
|
|
_lCurrentIndex(0),
|
|
_lMembersCount(0),
|
|
_pIDirObj(NULL),
|
|
_fRangeRetrieval(FALSE),
|
|
_fAllRetrieved(FALSE),
|
|
_pszRangeToFetch(NULL),
|
|
_pAttributeEntries(NULL),
|
|
_pCurrentEntry(NULL),
|
|
_dwCurRangeIndex(0),
|
|
_dwCurRangeMax(0),
|
|
_dwNumEntries(0),
|
|
_fLastSet(FALSE)
|
|
{
|
|
VariantInit( &_vMembers );
|
|
_pObjList = NULL;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Function:
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
CLDAPGroupCollectionEnum::CLDAPGroupCollectionEnum( ObjectTypeList ObjList )
|
|
: _Parent(NULL),
|
|
_ADsPath(NULL),
|
|
_GroupName(NULL),
|
|
_lCurrentIndex(0),
|
|
_lMembersCount(0)
|
|
{
|
|
VariantInit( &_vMembers );
|
|
_pObjList = NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Function:
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
CLDAPGroupCollectionEnum::~CLDAPGroupCollectionEnum()
|
|
{
|
|
VariantClear( &_vMembers );
|
|
delete _pObjList;
|
|
|
|
if ( _Parent )
|
|
ADsFreeString( _Parent );
|
|
|
|
if ( _GroupName )
|
|
ADsFreeString( _GroupName );
|
|
|
|
if ( _ADsPath )
|
|
ADsFreeString( _ADsPath );
|
|
|
|
if (_pIDirObj) {
|
|
_pIDirObj->Release();
|
|
}
|
|
|
|
if (_pszRangeToFetch) {
|
|
FreeADsStr(_pszRangeToFetch);
|
|
}
|
|
|
|
if (_pAttributeEntries) {
|
|
FreeADsMem(_pAttributeEntries);
|
|
}
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Function:
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
CLDAPGroupCollectionEnum::EnumGroupMembers(
|
|
ULONG cElements,
|
|
VARIANT FAR* pvar,
|
|
ULONG FAR* pcElementFetched
|
|
)
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
IDispatch *pDispatch = NULL;
|
|
DWORD i = 0;
|
|
|
|
IADs * pIADs = NULL;
|
|
BSTR pszClass = NULL;
|
|
DWORD dwClassID;
|
|
BSTR pszFilterName = NULL;
|
|
BOOL fFound = FALSE;
|
|
BOOL fEmpty = TRUE;
|
|
|
|
while (i < cElements) {
|
|
|
|
hr = GetUserMemberObject(&pDispatch);
|
|
if (FAILED(hr)) {
|
|
//
|
|
// Set hr to S_FALSE as all our enumerators are not
|
|
// built to handle a failure hr but only S_OK and S_FALSE.
|
|
//
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
if (hr == S_FALSE) {
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Apply the IADsMembers::put_Filter filter.
|
|
// If the enumerated object is not one of the types to be returned,
|
|
// go on to the next member of the group.
|
|
//
|
|
|
|
hr = pDispatch->QueryInterface(IID_IADs, (void **)&pIADs);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
//
|
|
// To check whether use specifies filter
|
|
//
|
|
fEmpty = _pObjList->IsEmpty();
|
|
|
|
//
|
|
// User specifies the filter
|
|
//
|
|
if (!fEmpty) {
|
|
|
|
//
|
|
// Determine the object class of the enumerated object and the corresponding
|
|
// object class ID number (as specified in the Filters global array).
|
|
//
|
|
hr = pIADs->get_Class(&pszClass);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
|
|
//
|
|
// Enumerate through the object classes listed in the user-specified filter
|
|
// until we either find a match (fFound = TRUE) or we reach the end of the
|
|
// list.
|
|
//
|
|
hr = _pObjList->Reset();
|
|
|
|
//
|
|
// compare with the user defined filter
|
|
//
|
|
while (SUCCEEDED(hr)) {
|
|
hr = _pObjList->GetCurrentObject(&pszFilterName);
|
|
|
|
if (SUCCEEDED(hr)
|
|
&& (!_wcsicmp(pszClass, pszFilterName))
|
|
) {
|
|
|
|
fFound = TRUE;
|
|
|
|
if(pszFilterName) {
|
|
SysFreeString(pszFilterName);
|
|
pszFilterName = NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if(pszFilterName) {
|
|
SysFreeString(pszFilterName);
|
|
pszFilterName = NULL;
|
|
}
|
|
|
|
hr = _pObjList->Next();
|
|
}
|
|
|
|
if (!fFound) {
|
|
//
|
|
// not on the list of objects to return, try again
|
|
// with the next member of the group
|
|
//
|
|
pDispatch->Release();
|
|
pDispatch = NULL;
|
|
|
|
pIADs->Release();
|
|
pIADs = NULL;
|
|
|
|
if (pszClass) {
|
|
ADsFreeString(pszClass);
|
|
pszClass = NULL;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (pszClass) {
|
|
ADsFreeString(pszClass);
|
|
pszClass = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
if(pIADs)
|
|
{
|
|
pIADs->Release();
|
|
pIADs = NULL;
|
|
|
|
}
|
|
|
|
//
|
|
// Return it.
|
|
//
|
|
VariantInit(&pvar[i]);
|
|
pvar[i].vt = VT_DISPATCH;
|
|
pvar[i].pdispVal = pDispatch;
|
|
(*pcElementFetched)++;
|
|
i++;
|
|
|
|
|
|
}
|
|
RRETURN_EXP_IF_ERR(hr);
|
|
|
|
error:
|
|
|
|
if (pDispatch) {
|
|
pDispatch->Release();
|
|
}
|
|
|
|
if (pIADs) {
|
|
pIADs->Release();
|
|
}
|
|
|
|
if (pszClass) {
|
|
ADsFreeString(pszClass);
|
|
}
|
|
|
|
RRETURN_EXP_IF_ERR(hr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Function:
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
CLDAPGroupCollectionEnum::GetUserMemberObject(
|
|
IDispatch ** ppDispatch
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
VARIANT v;
|
|
IUnknown *pObject = NULL;
|
|
TCHAR *pszADsPath = NULL;
|
|
LPWSTR pszUserName = NULL;
|
|
LPWSTR pszPassword = NULL;
|
|
DWORD dwAuthFlags = 0;
|
|
BOOL fRangeUsed = FALSE;
|
|
|
|
*ppDispatch = NULL;
|
|
|
|
VariantInit(&v);
|
|
|
|
hr = _Credentials.GetUserName(&pszUserName);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
hr = _Credentials.GetPassword(&pszPassword);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
dwAuthFlags = _Credentials.GetAuthFlags();
|
|
|
|
while ( TRUE )
|
|
{
|
|
VariantInit(&v);
|
|
|
|
if ( _lCurrentIndex >= _lMembersCount ) {
|
|
hr = S_FALSE;
|
|
//
|
|
// See if we need to fetch members using Rangeretrieval.
|
|
//
|
|
if (_fRangeRetrieval && !_fAllRetrieved) {
|
|
hr = GetNextMemberRange(&v);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if (hr == S_FALSE) {
|
|
goto error;
|
|
}
|
|
|
|
fRangeUsed = TRUE;
|
|
}
|
|
else
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// Variant v will have correct value already if range
|
|
// retrieval was used.
|
|
//
|
|
if (!fRangeUsed) {
|
|
|
|
if ( _vMembers.vt == VT_BSTR )
|
|
{
|
|
hr = VariantCopy( &v, &_vMembers );
|
|
}
|
|
else
|
|
{
|
|
hr = SafeArrayGetElement( V_ARRAY(&_vMembers), &_lCurrentIndex, &v);
|
|
}
|
|
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
}
|
|
|
|
_lCurrentIndex++;
|
|
|
|
LPTSTR pszMember = V_BSTR(&v);
|
|
LPTSTR pszTemp = NULL;
|
|
|
|
hr = BuildADsPathFromLDAPPath( _Parent, pszMember, &pszADsPath );
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
hr = ADsOpenObject(
|
|
pszADsPath,
|
|
pszUserName,
|
|
pszPassword,
|
|
dwAuthFlags,
|
|
IID_IUnknown,
|
|
(LPVOID *)&pObject
|
|
);
|
|
|
|
|
|
if ( pszADsPath )
|
|
{
|
|
FreeADsStr( pszADsPath );
|
|
pszADsPath = NULL;
|
|
}
|
|
|
|
VariantClear(&v);
|
|
|
|
//
|
|
// If we failed to get the current object, continue with the next one
|
|
//
|
|
if ( FAILED(hr))
|
|
continue;
|
|
|
|
hr = pObject->QueryInterface(
|
|
IID_IDispatch,
|
|
(LPVOID *) ppDispatch );
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
hr = S_OK;
|
|
goto error;
|
|
|
|
}
|
|
|
|
error:
|
|
|
|
if ( pObject )
|
|
pObject->Release();
|
|
|
|
if ( pszADsPath )
|
|
FreeADsStr( pszADsPath );
|
|
|
|
if (pszPassword) {
|
|
SecureZeroMemory(pszPassword, wcslen(pszPassword)*sizeof(WCHAR));
|
|
FreeADsStr(pszPassword);
|
|
}
|
|
|
|
if (pszUserName) {
|
|
FreeADsStr(pszUserName);
|
|
}
|
|
|
|
VariantClear(&v);
|
|
|
|
RRETURN(hr);
|
|
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CLDAPGroupCollectionEnum::Next
|
|
//
|
|
// Synopsis: Returns cElements number of requested NetOle objects in the
|
|
// array supplied in pvar.
|
|
//
|
|
// Arguments: [cElements] -- The number of elements requested by client
|
|
// [pvar] -- ptr to array of VARIANTs to for return objects
|
|
// [pcElementFetched] -- if non-NULL, then number of elements
|
|
// -- actually returned is placed here
|
|
//
|
|
// Returns: HRESULT -- S_OK if number of elements requested are returned
|
|
// -- S_FALSE if number of elements is < requested
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 11-3-95 krishnag Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CLDAPGroupCollectionEnum::Next(
|
|
ULONG cElements,
|
|
VARIANT FAR* pvar,
|
|
ULONG FAR* pcElementFetched
|
|
)
|
|
{
|
|
ULONG cElementFetched = 0;
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = EnumGroupMembers(
|
|
cElements,
|
|
pvar,
|
|
&cElementFetched
|
|
);
|
|
|
|
if (pcElementFetched) {
|
|
*pcElementFetched = cElementFetched;
|
|
}
|
|
RRETURN_EXP_IF_ERR(hr);
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CLDAPGroupCollectionEnum::UpdateRangeToFetch()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WCHAR szPath[512];
|
|
|
|
//
|
|
// szPath should be big enough to handle any range we
|
|
// can reasonably expect and will be used to build the
|
|
// member string.
|
|
//
|
|
|
|
if (_pszRangeToFetch == NULL) {
|
|
//
|
|
// Rather than ask for the first n elements again,
|
|
// we can use the count we have in the variant array
|
|
// to decide where we need to start.
|
|
//
|
|
|
|
wsprintf(szPath, L"member;range=%d-*", _lMembersCount);
|
|
|
|
_pszRangeToFetch = AllocADsStr(szPath);
|
|
if (!_pszRangeToFetch) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// In this case the call to GetObjectAttr has been made
|
|
// and we need to get the info out of the name.
|
|
//
|
|
BOOL fUpdated = FALSE;
|
|
for (DWORD i = 0; (i < _dwNumEntries) && !fUpdated; i++) {
|
|
LPWSTR pszTemp = NULL;
|
|
LPWSTR pszAttrName = _pAttributeEntries[i].pszAttrName;
|
|
LPWSTR pszStar = NULL;
|
|
|
|
if (wcslen(pszAttrName) > wcslen(L"member;range=")) {
|
|
//
|
|
// See if we have our string
|
|
//
|
|
if (!_wcsnicmp(
|
|
pszAttrName,
|
|
L"member;range=",
|
|
wcslen(L"member;range")
|
|
)
|
|
) {
|
|
|
|
_pCurrentEntry = &(_pAttributeEntries[i]);
|
|
_dwCurRangeMax = _pCurrentEntry->dwNumValues;
|
|
_dwCurRangeIndex = 0;
|
|
pszTemp = wcschr(pszAttrName, L'=');
|
|
|
|
if (!pszTemp) {
|
|
//
|
|
// No chance of recovery from this.
|
|
//
|
|
BAIL_ON_FAILURE(hr = E_FAIL);
|
|
}
|
|
|
|
//
|
|
// Move the lower part of range.
|
|
//
|
|
*pszTemp++;
|
|
if (!*pszTemp) {
|
|
BAIL_ON_FAILURE(hr = E_FAIL);
|
|
}
|
|
|
|
pszStar = wcschr(pszTemp, L'*');
|
|
if (pszStar) {
|
|
//
|
|
// Do not bother with any udpate of the range,
|
|
// we have all the entries.
|
|
//
|
|
_fLastSet = TRUE;
|
|
goto error;
|
|
}
|
|
|
|
DWORD dwLower = 0;
|
|
DWORD dwHigher = 0;
|
|
|
|
if (!swscanf(pszTemp, L"%d-%d", &dwLower, &dwHigher)) {
|
|
BAIL_ON_FAILURE(hr = E_FAIL);
|
|
}
|
|
|
|
dwHigher++;
|
|
wsprintf(szPath, L"member;range=%d-*", dwHigher);
|
|
|
|
FreeADsStr(_pszRangeToFetch);
|
|
_pszRangeToFetch = AllocADsStr(szPath);
|
|
if (!_pszRangeToFetch) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
//
|
|
// Set flag so we can get out of the loop.
|
|
//
|
|
fUpdated = TRUE;
|
|
|
|
} // this was not member;
|
|
|
|
} // is the length greater than that of member;
|
|
|
|
} // for each entry in attribute entries.
|
|
|
|
if (!fUpdated) {
|
|
//
|
|
// Failed cause there was no members or a range.
|
|
//
|
|
BAIL_ON_FAILURE(hr = E_FAIL);
|
|
}
|
|
} // _pszRangeToFetch was non NULL
|
|
|
|
|
|
error:
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CLDAPGroupCollectionEnum::GetNextMemberRange
|
|
//
|
|
// Synopsis: Returns a variant bstr with the dn of the next member in
|
|
// the group or FALSE if there are no more. This routine will
|
|
// will use IDirectoryObject to fetch more members if applicable.
|
|
//
|
|
//
|
|
// Arguments: [pVarMemberBstr] -- ptr to VARIANT for return bstr.
|
|
//
|
|
// Returns: HRESULT -- S_OK if number of elements requested are returned
|
|
// -- S_FALSE if number of elements is < requested
|
|
// -- Other failure hr's.
|
|
// Modifies:
|
|
//
|
|
// History: 9-12-99 AjayR Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
CLDAPGroupCollectionEnum::GetNextMemberRange(
|
|
VARIANT FAR* pVarMemberBstr
|
|
)
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (_fAllRetrieved) {
|
|
RRETURN(S_FALSE);
|
|
}
|
|
|
|
//
|
|
// Initialize the range to fetch if applicable.
|
|
//
|
|
if (_pszRangeToFetch == NULL) {
|
|
hr = UpdateRangeToFetch();
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
if (_dwCurRangeIndex == _dwCurRangeMax) {
|
|
//
|
|
// Call into wrapper for GetObjectAttributes.
|
|
//
|
|
if (_fLastSet) {
|
|
_fAllRetrieved = TRUE;
|
|
hr = S_FALSE;
|
|
}
|
|
else {
|
|
hr = UpdateAttributeEntries();
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
if (hr == S_FALSE) {
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
//
|
|
// At this point we should have the entries in our current
|
|
// return set.
|
|
//
|
|
if (!_pCurrentEntry) {
|
|
BAIL_ON_FAILURE(hr = E_FAIL);
|
|
}
|
|
|
|
if (_dwCurRangeIndex < _dwCurRangeMax) {
|
|
|
|
hr = ADsAllocString(
|
|
_pCurrentEntry->pADsValues[_dwCurRangeIndex].DNString,
|
|
&V_BSTR(pVarMemberBstr)
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
_dwCurRangeIndex++;
|
|
|
|
error:
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
HRESULT
|
|
CLDAPGroupCollectionEnum::UpdateAttributeEntries()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPWSTR aStrings[2];
|
|
|
|
ADsAssert(_pszRangeToFetch || !"Range is NULL internal error");
|
|
|
|
if (_pAttributeEntries) {
|
|
FreeADsMem(_pAttributeEntries);
|
|
_pAttributeEntries = NULL;
|
|
}
|
|
|
|
aStrings[0] = _pszRangeToFetch;
|
|
aStrings[1] = NULL;
|
|
|
|
|
|
hr = _pIDirObj->GetObjectAttributes(
|
|
aStrings,
|
|
1,
|
|
&_pAttributeEntries,
|
|
&_dwNumEntries
|
|
);
|
|
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if (_dwNumEntries == 0) {
|
|
hr = S_FALSE;
|
|
}
|
|
else {
|
|
//
|
|
// Will return error if member was not there.
|
|
//
|
|
hr = UpdateRangeToFetch();
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
error:
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
|