|
|
// 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; }
|