Leaked source code of windows server 2003
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.
 
 
 
 
 
 

827 lines
24 KiB

// query.cpp
#include "stdafx.h"
#include <cmnquery.h>
#include <dsquery.h>
#include <shlobj.h>
#include <dsclient.h>
#include <iads.h>
#include <adshlp.h>
#define SECURITY_WIN32
#include <security.h> // TranslateName
#include <lmcons.h>
#include <lmapibuf.h> // NetApiBufferFree
#include <dsgetdc.h> // DsGetDCName
#include "query.h"
#include "rowitem.h"
#include "resource.h"
#include "namemap.h"
#include "atlgdi.h"
#include "util.h"
#include <list>
struct __declspec( uuid("ab50dec0-6f1d-11d0-a1c4-00aa00c16e65")) ICommonQuery;
typedef const BYTE* LPCBYTE;
HRESULT GetQuery(tstring& strScope, tstring& strQuery, byte_string& bsQueryData, HWND hWnd)
{
// Get instance of common query object
CComQIPtr<ICommonQuery, &IID_ICommonQuery> spQuery;
HRESULT hr = spQuery.CoCreateInstance(CLSID_CommonQuery, NULL, CLSCTX_INPROC_SERVER);
RETURN_ON_FAILURE(hr);
// Structure for DSQuery handler
DSQUERYINITPARAMS dqip;
memset(&dqip, 0, sizeof(dqip));
dqip.cbStruct = sizeof(dqip);
dqip.dwFlags = DSQPF_NOSAVE | DSQPF_ENABLEADMINFEATURES | DSQPF_ENABLEADVANCEDFEATURES;
dqip.pDefaultScope = (LPTSTR)strScope.c_str();
// Structure for common query
OPENQUERYWINDOW oqw;
memset(&oqw, 0, sizeof(oqw));
oqw.cbStruct = sizeof(oqw);
oqw.dwFlags = OQWF_SHOWOPTIONAL | OQWF_OKCANCEL | OQWF_HIDESEARCHUI | OQWF_SAVEQUERYONOK | OQWF_HIDEMENUS;
oqw.clsidHandler = CLSID_DsQuery;
oqw.pHandlerParameters = &dqip;
CPersistQuery persistQuery;
oqw.pPersistQuery = (IPersistQuery*)&persistQuery;
if( !bsQueryData.empty() )
{
persistQuery.Load(bsQueryData, strScope);
oqw.dwFlags |= OQWF_LOADQUERY;
}
CComPtr<IDataObject> spDO;
hr = spQuery->OpenQueryWindow(hWnd, &oqw, &spDO);
// if failed to open query window on persisted query
if( FAILED(hr) && !bsQueryData.empty() )
{
// See if there is a problem with the scope
CComPtr<IUnknown> spUnk;
if( ADsOpenObject(strScope.c_str(), NULL, NULL, ADS_SECURE_AUTHENTICATION, IID_IUnknown, (LPVOID*)&spUnk) != S_OK )
{
// if so, try again with a null scope
tstring strNullScope;
persistQuery.Load(bsQueryData, strNullScope);
hr = spQuery->OpenQueryWindow(hWnd, &oqw, &spDO);
}
}
if( SUCCEEDED(hr) && spDO != NULL )
{
FORMATETC fmte = {0, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
STGMEDIUM medium = { TYMED_NULL, NULL, NULL};
static UINT s_cfDsQueryParams = 0;
if( !s_cfDsQueryParams )
s_cfDsQueryParams = RegisterClipboardFormat(CFSTR_DSQUERYPARAMS);
fmte.cfFormat = (CLIPFORMAT)s_cfDsQueryParams;
if( SUCCEEDED(spDO->GetData(&fmte, &medium)) )
{
LPDSQUERYPARAMS pDsQueryParams = (LPDSQUERYPARAMS)medium.hGlobal;
LPWSTR pFilter = (LPWSTR)((CHAR*)pDsQueryParams + pDsQueryParams->offsetQuery);
strQuery = pFilter;
ReleaseStgMedium(&medium);
}
static UINT s_cfDsQueryScope = 0;
if( !s_cfDsQueryScope )
s_cfDsQueryScope = RegisterClipboardFormat(CFSTR_DSQUERYSCOPE);
fmte.cfFormat = (CLIPFORMAT)s_cfDsQueryScope;
if( SUCCEEDED(spDO->GetData(&fmte, &medium)) )
{
strScope = (LPWSTR)medium.hGlobal;
ReleaseStgMedium(&medium);
}
persistQuery.Save(bsQueryData);
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////////////////
// CPersistQuery
//
/*-----------------------------------------------------------------------------
/ Constructor / IUnknown methods
/----------------------------------------------------------------------------*/
CPersistQuery::CPersistQuery()
{
m_cRefCount = 1;
}
//
// IUnknown methods
//
STDMETHODIMP CPersistQuery::QueryInterface(REFIID riid, LPVOID* ppvObject)
{
VALIDATE_POINTER( ppvObject );
if( IsEqualIID(riid, IID_IPersistQuery) )
{
*ppvObject = (LPVOID)(IPersistQuery*)this;
return S_OK;
}
return E_NOTIMPL;
}
STDMETHODIMP_(ULONG) CPersistQuery::AddRef()
{
return ++m_cRefCount;
}
STDMETHODIMP_(ULONG) CPersistQuery::Release()
{
if( --m_cRefCount == 0 )
{
delete this;
return 0;
}
return m_cRefCount;
}
/*-----------------------------------------------------------------------------
/ IPersist methods
/----------------------------------------------------------------------------*/
STDMETHODIMP CPersistQuery::GetClassID(THIS_ CLSID* pClassID)
{
return E_NOTIMPL;
}
/*-----------------------------------------------------------------------------
/ IPersistQuery methods
/----------------------------------------------------------------------------*/
STDMETHODIMP CPersistQuery::WriteString(LPCTSTR pSection, LPCTSTR pKey, LPCTSTR pValue)
{
if( pValue == NULL )
return E_INVALIDARG;
return WriteStruct(pSection, pKey, (LPVOID)pValue, (wcslen(pValue) + 1) * sizeof(WCHAR));
}
STDMETHODIMP CPersistQuery::WriteInt(LPCTSTR pSection, LPCTSTR pKey, INT value)
{
return WriteStruct(pSection, pKey, (LPVOID)&value, sizeof(int));
}
STDMETHODIMP CPersistQuery::WriteStruct(LPCTSTR pSection, LPCTSTR pKey, LPVOID pStruct, DWORD cbStruct)
{
if( pSection == NULL || pKey == NULL || pStruct == NULL )
return E_INVALIDARG;
LPBYTE pData = (LPBYTE)malloc(cbStruct + sizeof(DWORD));
if( !pData ) return E_OUTOFMEMORY;
*(LPDWORD)pData = cbStruct;
memcpy(pData + sizeof(DWORD), pStruct, cbStruct);
m_mapQueryData[pSection][pKey] = std::auto_ptr<BYTE>(pData);
return S_OK;
}
STDMETHODIMP CPersistQuery::ReadString(LPCTSTR pSection, LPCTSTR pKey, LPTSTR pBuffer, INT cchBuffer)
{
return ReadStruct(pSection, pKey, (LPVOID)pBuffer, cchBuffer);
}
STDMETHODIMP CPersistQuery::ReadInt(LPCTSTR pSection, LPCTSTR pKey, LPINT pValue)
{
return ReadStruct(pSection, pKey, (LPVOID)pValue, sizeof(INT));
}
STDMETHODIMP CPersistQuery::ReadStruct(LPCTSTR pSection, LPCTSTR pKey, LPVOID pStruct, DWORD cbStruct)
{
VALIDATE_POINTER( pStruct );
QueryDataMap::iterator itDataMap = m_mapQueryData.find(pSection);
if( itDataMap == m_mapQueryData.end() )
return E_FAIL;
QuerySectionMap::iterator itSecMap = itDataMap->second.find(pKey);
if( itSecMap == itDataMap->second.end() )
return E_FAIL;
LPBYTE pData = itSecMap->second.get();
DWORD cbData = pData ? *(LPDWORD)pData : 0;
if( cbData > cbStruct )
return E_FAIL;
// return value
memcpy(pStruct, pData + sizeof(DWORD), cbData);
return S_OK;
}
STDMETHODIMP CPersistQuery::Clear()
{
m_mapQueryData.clear();
return S_OK;
}
void PutString(byte_string& strOut, const tstring& strData)
{
DWORD dwLen = strData.size();
strOut.append((LPBYTE)&dwLen, sizeof(DWORD));
strOut.append((LPBYTE)strData.data(), dwLen * sizeof(WCHAR));
}
HRESULT CPersistQuery::Save(byte_string& strOut)
{
strOut.resize(0);
DWORD dwSize;
QueryDataMap::iterator itDataMap;
for( itDataMap = m_mapQueryData.begin(); itDataMap != m_mapQueryData.end(); itDataMap++ )
{
const tstring& strSecName = itDataMap->first;
QuerySectionMap& SecMap = itDataMap->second;
PutString(strOut, strSecName);
QuerySectionMap::iterator itSecMap;
for( itSecMap = SecMap.begin(); itSecMap != SecMap.end(); itSecMap++ )
{
const tstring& strValueName = itSecMap->first;
LPBYTE pData = itSecMap->second.get();
if( pData )
{
PutString(strOut, strValueName);
strOut.append(pData, *(LPDWORD)pData + sizeof(DWORD));
}
}
dwSize = 0;
strOut.append((LPBYTE)&dwSize, sizeof(DWORD));
}
dwSize = 0;
strOut.append((LPBYTE)&dwSize, sizeof(DWORD));
return S_OK;
}
BOOL GetString(LPCBYTE& pbIn, tstring& strData)
{
if( !pbIn ) return FALSE;
DWORD dwLen = *(LPDWORD)pbIn;
pbIn += sizeof(DWORD);
if( dwLen != 0 )
{
strData.assign((LPWSTR)pbIn, dwLen);
pbIn += dwLen * sizeof(WCHAR);
}
return(dwLen != 0);
}
HRESULT CPersistQuery::Load(byte_string& strIn, tstring& strScope)
{
m_mapQueryData.clear();
LPCBYTE pData = strIn.data();
if( !pData ) return E_INVALIDARG;
tstring strSecName;
while( GetString(pData, strSecName) )
{
QuerySectionMap& SecMap = m_mapQueryData[strSecName];
tstring strName;
while( GetString(pData, strName) )
{
DWORD dwSize = *(LPDWORD)pData + sizeof(DWORD);
if( strName == _T("Value0") )
{
tstring strQueryTmp = (LPCWSTR)(pData+sizeof(DWORD));
ExpandDCWildCard(strQueryTmp);
DWORD dwStringSize = strQueryTmp.size() ? ((strQueryTmp.size() + 1) * sizeof(wchar_t)) : 0;
LPDWORD pdwBuf = (LPDWORD)malloc(dwStringSize + sizeof(DWORD));
if( pdwBuf == NULL )
{
return E_OUTOFMEMORY;
}
pdwBuf[0] = dwStringSize;
memcpy(pdwBuf+1, strQueryTmp.c_str(), dwStringSize);
SecMap[strName] = std::auto_ptr<BYTE>((LPBYTE)pdwBuf);
}
else
{
LPBYTE pBuf = (LPBYTE)malloc(dwSize);
if( pBuf == NULL )
return E_OUTOFMEMORY;
memcpy(pBuf, pData, dwSize);
SecMap[strName] = std::auto_ptr<BYTE>(pBuf);
}
pData += dwSize;
}
// if DsQuery section, override the persisted scope & scope size values
// with our own. This is necessary when the local scope option is specified
// because then the scope is determined at run-time and may be different than
// the persisted value.
if( strSecName == _T("DsQuery") )
{
DWORD dwScopeSize = strScope.size() ? ((strScope.size() + 1) * sizeof(wchar_t)) : 0;
// add scope size integer equal to byte length of scope string
LPDWORD pdwBuf = (LPDWORD)malloc(2 * sizeof(DWORD));
if( pdwBuf == NULL )
{
return E_OUTOFMEMORY;
}
pdwBuf[0] = sizeof(DWORD);
pdwBuf[1] = dwScopeSize;
SecMap[_T("ScopeSize")] = std::auto_ptr<BYTE>((LPBYTE)pdwBuf);
// add scope string value
if( dwScopeSize )
{
pdwBuf = (LPDWORD)malloc(dwScopeSize + sizeof(DWORD));
if( pdwBuf == NULL )
{
return E_OUTOFMEMORY;
}
pdwBuf[0] = dwScopeSize;
memcpy(pdwBuf+1, strScope.c_str(), dwScopeSize);
SecMap[_T("Scope")] = std::auto_ptr<BYTE>((LPBYTE)pdwBuf);
}
}
}
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// Query Utility Functions
//
HRESULT GetQueryScope(HWND hDlg, tstring& strScope)
{
DSBROWSEINFO dsbi;
TCHAR szBuffer[MAX_PATH] = {0};
tstring strCaption = StrLoadString(IDS_SCOPEBROWSE_CAPTION);
tstring strTitle = StrLoadString(IDS_SELECTSCOPE);
dsbi.cbStruct = sizeof(dsbi);
dsbi.hwndOwner = hDlg;
dsbi.pszCaption = strCaption.c_str();
dsbi.pszTitle = strTitle.c_str();
dsbi.pszRoot = NULL;
dsbi.pszPath = szBuffer;
dsbi.cchPath = lengthof(szBuffer);
dsbi.dwFlags = DSBI_ENTIREDIRECTORY;
dsbi.pfnCallback = NULL;
dsbi.lParam = (LPARAM)0;
if( !strScope.empty() && strScope.size() < MAX_PATH )
{
lstrcpyn( szBuffer, strScope.c_str(), MAX_PATH );
dsbi.dwFlags |= DSBI_EXPANDONOPEN;
}
HRESULT hr = S_FALSE;
if( IDOK == DsBrowseForContainer(&dsbi) )
{
strScope = szBuffer;
hr = S_OK;
}
return hr;
}
void GetScopeDisplayString(tstring& strScope, tstring& strDisplay)
{
strDisplay.erase();
if( !strScope.empty() )
{
LPCWSTR pszScope = strScope.c_str();
// Special case for GC: use display name "Entire Directory"
if( _wcsnicmp(L"GC:", pszScope, 3) == 0 )
{
CString strDir;
strDir.LoadString(IDS_ENTIRE_DIRECTORY);
strDisplay = strDir;
}
else
{
if( _wcsnicmp(L"LDAP://", pszScope, 7) == 0 )
pszScope += 7;
WCHAR szBuf[MAX_PATH];
ULONG cBuf = MAX_PATH;
BOOL bStat = TranslateName(pszScope, NameFullyQualifiedDN, NameCanonical, szBuf, &cBuf);
if( bStat )
{
int nLen = wcslen(szBuf);
if( (nLen > 0) && (szBuf[nLen-1] == L'/') )
nLen--;
strDisplay.assign(szBuf, nLen);
}
else
{
strDisplay = strScope;
}
}
}
}
void GetFullyQualifiedScopeString(tstring& strScope, tstring& strQualified)
{
strQualified.erase();
if( !strScope.empty() )
{
// TranslateName expects a trailing '/' on a canonical domain name
tstring strTmp = strScope;
strTmp += L"/";
WCHAR* pszBuf = NULL;
ULONG cBuf = 0;
BOOL bStat = TranslateName(strTmp.c_str(), NameCanonical, NameFullyQualifiedDN, pszBuf, &cBuf);
if( cBuf )
{
pszBuf = new WCHAR[cBuf];
if( pszBuf )
{
bStat = TranslateName(strTmp.c_str(), NameCanonical, NameFullyQualifiedDN, pszBuf, &cBuf);
}
}
if( bStat )
{
strQualified = L"LDAP://";
strQualified += pszBuf;
}
else
{
strQualified = strScope;
}
if( pszBuf )
{
delete [] pszBuf;
}
}
}
LPCWSTR GetLocalDomain()
{
static tstring strLocDomain;
if( !strLocDomain.empty() )
return strLocDomain.c_str();
DOMAIN_CONTROLLER_INFO* pDcInfo = NULL;
DWORD dwStat = DsGetDcName(NULL, NULL, NULL, NULL, DS_DIRECTORY_SERVICE_REQUIRED|DS_RETURN_DNS_NAME, &pDcInfo);
if( dwStat == NO_ERROR && pDcInfo != NULL )
{
tstring str = pDcInfo->DomainName;
GetFullyQualifiedScopeString(str, strLocDomain);
NetApiBufferFree(pDcInfo);
}
return strLocDomain.c_str();
}
HRESULT GetNamingContext(NameContextType ctx, LPCWSTR* ppszContextDN)
{
VALIDATE_POINTER( ppszContextDN );
const static LPCWSTR pszContextName[NAMECTX_COUNT] = { L"schemaNamingContext", L"configurationNamingContext"};
static tstring strContextDN[NAMECTX_COUNT];
HRESULT hr = S_OK;
if( strContextDN[ctx].empty() )
{
CComVariant var;
CComPtr<IADs> pObj;
hr = ADsGetObject(L"LDAP://rootDSE", IID_IADs, (void**)&pObj);
if( SUCCEEDED(hr) )
{
CComBSTR bstrProp = const_cast<LPWSTR>(pszContextName[ctx]);
hr = pObj->Get( bstrProp, &var );
if( SUCCEEDED(hr) )
{
strContextDN[ctx] = var.bstrVal;
*ppszContextDN = strContextDN[ctx].c_str();
}
}
}
else
{
*ppszContextDN = strContextDN[ctx].c_str();
hr = S_OK;
}
return hr;
}
HRESULT GetClassesOfCategory(IDirectorySearch* pDirSrch, tstring& strCategory, std::set<tstring>& setClasses)
{
VALIDATE_POINTER( pDirSrch );
// Form query filter for class with class/category name
tstring strFilter = L"(&(objectCategory=classSchema)(ldapDisplayName=";
strFilter += strCategory;
strFilter += L"))";
// Query for category that class belongs to
ADS_SEARCH_HANDLE hSearch;
LPWSTR pszDn = L"defaultObjectCategory";
HRESULT hr = pDirSrch->ExecuteSearch(const_cast<LPWSTR>(strFilter.c_str()), &pszDn, 1, &hSearch);
if( SUCCEEDED(hr) )
{
hr = pDirSrch->GetFirstRow(hSearch);
if( hr == S_OK )
{
ADS_SEARCH_COLUMN col;
hr = pDirSrch->GetColumn(hSearch, pszDn, &col);
if( SUCCEEDED(hr) )
{
// Form query filter for all structure classes belonging to this category
strFilter = L"(&(objectCategory=classSchema)(objectClassCategory=1)(defaultObjectCategory=";
strFilter += col.pADsValues->DNString;
strFilter += L"))";
// Query for LDAP name of each class
ADS_SEARCH_HANDLE hSearch2;
LPWSTR pszName = L"ldapDisplayName";
hr = pDirSrch->ExecuteSearch(const_cast<LPWSTR>(strFilter.c_str()), &pszName, 1, &hSearch2);
if( SUCCEEDED(hr) )
{
HRESULT hr2;
while( (hr2 = pDirSrch->GetNextRow(hSearch2)) == S_OK )
{
ADS_SEARCH_COLUMN col2;
hr2 = pDirSrch->GetColumn(hSearch2, pszName, &col2);
if( SUCCEEDED(hr2) )
{
setClasses.insert(col2.pADsValues->CaseIgnoreString);
pDirSrch->FreeColumn(&col2);
}
}
pDirSrch->CloseSearchHandle(hSearch2);
}
pDirSrch->FreeColumn(&col);
}
}
pDirSrch->CloseSearchHandle(hSearch);
}
return hr;
}
HRESULT GetSubclassesOfClass(IDirectorySearch* pDirSrch, tstring& strClass, std::set<tstring>& setClasses)
{
VALIDATE_POINTER( pDirSrch );
// Form query filter for classes that derive from this class
tstring strFilter = L"(&(objectCategory=classSchema)(subClassOf=";
strFilter += strClass;
strFilter += L"))";
// Get display names of subclasses
ADS_SEARCH_HANDLE hSearch;
LPWSTR pszName = L"lDAPDisplayName";
HRESULT hr = pDirSrch->ExecuteSearch(const_cast<LPWSTR>(strFilter.c_str()), &pszName, 1, &hSearch);
if( SUCCEEDED(hr) )
{
while( (hr = pDirSrch->GetNextRow(hSearch)) == S_OK )
{
ADS_SEARCH_COLUMN col;
hr = pDirSrch->GetColumn(hSearch, pszName, &col);
if( SUCCEEDED(hr) )
{
tstring strSubclass = col.pADsValues->CaseIgnoreString;
pDirSrch->FreeColumn(&col);
setClasses.insert(strSubclass);
GetSubclassesOfClass(pDirSrch, strSubclass, setClasses);
}
}
pDirSrch->CloseSearchHandle( hSearch );
}
return hr;
}
HRESULT GetQueryClasses(tstring& strQuery, std::set<tstring>& setClasses)
{
// Create a schema directory search object
LPCWSTR pszSchemaDN;
HRESULT hr = GetNamingContext(NAMECTX_SCHEMA, &pszSchemaDN);
RETURN_ON_FAILURE(hr);
tstring strScope = L"LDAP://";
strScope += pszSchemaDN;
CComPtr<IDirectorySearch> spDirSrch;
hr = ADsOpenObject(strScope.c_str(), NULL, NULL, ADS_SECURE_AUTHENTICATION, IID_IDirectorySearch, (LPVOID*)&spDirSrch);
RETURN_ON_FAILURE(hr)
// Set search preferences
ADS_SEARCHPREF_INFO prefInfo[2];
prefInfo[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE; // sub-tree search
prefInfo[0].vValue.dwType = ADSTYPE_INTEGER;
prefInfo[0].vValue.Integer = ADS_SCOPE_ONELEVEL;
prefInfo[1].dwSearchPref = ADS_SEARCHPREF_PAGESIZE; // paged results
prefInfo[1].vValue.dwType = ADSTYPE_INTEGER;
prefInfo[1].vValue.Integer = 64;
hr = spDirSrch->SetSearchPreference( prefInfo, lengthof(prefInfo) );
RETURN_ON_FAILURE(hr)
#define CAT_PROP L"(objectCategory="
#define CLS_PROP L"(objectClass="
UINT uPos = 0;
while( (uPos = strQuery.find(CAT_PROP, uPos)) != tstring::npos )
{
uPos += wcslen(CAT_PROP);
UINT uEnd = strQuery.find(L")", uPos);
if( uEnd != tstring::npos )
{
tstring strCat = strQuery.substr(uPos, uEnd - uPos);
hr = GetClassesOfCategory(spDirSrch, strCat, setClasses);
}
uPos = uEnd;
}
uPos = 0;
while( (uPos = strQuery.find(CLS_PROP, uPos)) != tstring::npos )
{
uPos += wcslen(CLS_PROP);
UINT uEnd = strQuery.find(L")", uPos);
if( uEnd != tstring::npos )
{
tstring strClass = strQuery.substr(uPos, uEnd - uPos);
setClasses.insert(strClass);
hr = GetSubclassesOfClass(spDirSrch, strClass, setClasses);
}
uPos = uEnd;
}
// get lower case version of query string
LPWSTR pszQueryLC = new WCHAR[(strQuery.size() + 1)];
if( !pszQueryLC ) return E_OUTOFMEMORY;
wcscpy(pszQueryLC, strQuery.c_str());
_wcslwr(pszQueryLC);
// check for non-class related queries generated by DSQuery
if( wcsstr(pszQueryLC, L"(ou>=\"\")") != NULL )
setClasses.insert(L"organizationalUnit");
if( wcsstr(pszQueryLC, L"(samaccounttype=805306369)") != NULL ||
wcsstr(pszQueryLC, L"(primarygroupid=516)") != NULL )
setClasses.insert(L"computer");
delete [] pszQueryLC;
return hr;
}
HRESULT FindClassObject(LPCWSTR pszClass, tstring& strObjPath)
{
VALIDATE_POINTER( pszClass );
CComPtr<IDirectorySearch> spDirSrch;
HRESULT hr = ADsOpenObject(GetLocalDomain(), NULL, NULL, ADS_SECURE_AUTHENTICATION, IID_IDirectorySearch, (LPVOID*)&spDirSrch);
RETURN_ON_FAILURE(hr)
// Set search preferences - search sub-tree for single object
ADS_SEARCHPREF_INFO prefInfo[2];
prefInfo[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE; // sub-tree search
prefInfo[0].vValue.dwType = ADSTYPE_INTEGER;
prefInfo[0].vValue.Integer = ADS_SCOPE_SUBTREE;
prefInfo[1].dwSearchPref = ADS_SEARCHPREF_SIZE_LIMIT; // single object
prefInfo[1].vValue.dwType = ADSTYPE_INTEGER;
prefInfo[1].vValue.Integer = 1;
hr = spDirSrch->SetSearchPreference(prefInfo, lengthof(prefInfo));
RETURN_ON_FAILURE(hr)
// Set filter string to (&(ObjectCategory=class_name)(objectClass=class_name))
tstring strFilter = L"(&(objectCategory=";
strFilter += pszClass;
strFilter += L")(objectClass=";
strFilter += pszClass;
strFilter += L"))";
// Query for distinguished name of class
ADS_SEARCH_HANDLE hSearch;
LPWSTR pszDn = L"distinguishedName";
hr = spDirSrch->ExecuteSearch(const_cast<LPWSTR>(strFilter.c_str()), &pszDn, 1, &hSearch);
if( SUCCEEDED(hr) )
{
hr = spDirSrch->GetFirstRow(hSearch);
if( hr == S_OK )
{
ADS_SEARCH_COLUMN col;
hr = spDirSrch->GetColumn(hSearch, pszDn, &col);
if( SUCCEEDED(hr) )
{
strObjPath = col.pADsValues->DNString;
spDirSrch->FreeColumn(&col);
}
}
spDirSrch->CloseSearchHandle(hSearch);
}
return hr;
}