|
|
#include "pch.h"
#include "stddef.h"
#pragma hdrstop
/*-----------------------------------------------------------------------------
/ Query thread bits /----------------------------------------------------------------------------*/
//
// Page size used for paging the result sets (the LDAP server core is a sync process)
// therefore getting it to return smaller result blobs is better for us
//
#define PAGE_SIZE 64
#define MAX_RESULT 10000
//
// When the query is issued we always pull back at least ADsPath and objectClass
// as properites (these are required for the viewer to work). Therefore these define
// the mapping of these values to the returned column set.
//
#define PROPERTYMAP_ADSPATH 0
#define PROPERTYMAP_OBJECTCLASS 1
#define PROPERTYMAP_USER 2
#define INDEX_TO_PROPERTY(i) (i+PROPERTYMAP_USER)
//
// THREADDATA this is the state structure for the thread, it encapsulates
// the parameters and other junk required to the keep the thread alive.
//
typedef struct { LPTHREADINITDATA ptid; INT cProperties; // number of properties in aProperties
LPWSTR* aProperties; // array of string pointers for "property names"
INT cColumns; // number of columsn in view
INT* aColumnToPropertyMap; // mapping from display column index to property name
} THREADDATA, * LPTHREADDATA;
//
// Helper macro to send messages for the fg view, including the reference
// count
//
#define SEND_VIEW_MESSAGE(ptid, uMsg, lParam) \
SendMessage(GetParent(ptid->hwndView), uMsg, (ptid)->dwReference, lParam)
//
// Function prototypes for the query thread engine.
//
HRESULT QueryThread_IssueQuery(LPTHREADDATA ptd); HRESULT QueryThread_BuildPropertyList(LPTHREADDATA ptd); VOID QueryThread_FreePropertyList(LPTHREADDATA ptd);
/*-----------------------------------------------------------------------------
/ Helper functions /----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
/ QueryThread_GetFilter / ---------------------- / Construct the LDAP filter we are going to use for this query. / / In: / ppQuery -> receives the full filter / pBaseFilter -> filter string to use as base / fShowHidden = show hidden objects? / / Out: / HRESULT /----------------------------------------------------------------------------*/
VOID _GetFilter(LPWSTR pFilter, UINT* pcchFilterLen, LPWSTR pBaseFilter, BOOL fShowHidden) { HRESULT hr;
TraceEnter(TRACE_QUERYTHREAD, "_GetFilter");
if (pFilter) *pFilter = TEXT('\0');
PutStringElementW(pFilter, pcchFilterLen, L"(&");
if (!fShowHidden) PutStringElementW(pFilter, pcchFilterLen, c_szShowInAdvancedViewOnly);
PutStringElementW(pFilter, pcchFilterLen, pBaseFilter); PutStringElementW(pFilter, pcchFilterLen, L")");
TraceLeave(); }
HRESULT QueryThread_GetFilter(LPWSTR* ppFilter, LPWSTR pBaseFilter, BOOL fShowHidden) { HRESULT hr; UINT cchFilterLen = 0;
TraceEnter(TRACE_QUERYTHREAD, "QueryThread_GetFilter");
_GetFilter(NULL, &cchFilterLen, pBaseFilter, fShowHidden);
hr = LocalAllocStringLenW(ppFilter, cchFilterLen); FailGracefully(hr, "Failed to allocate buffer for query string"); _GetFilter(*ppFilter, NULL, pBaseFilter, fShowHidden); hr = S_OK;
exit_gracefully: TraceLeaveResult(hr); }
/*-----------------------------------------------------------------------------
/ Background query thread, this does the work of issuing the query and then / populating the view. /----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
/ QueryThread / ----------- / Thread function sits spinning its wheels waiting for a query to be / received from the outside world. The main result viewer communicates / with this code by ThreadSendMessage. / / In: / pThreadParams -> structure that defines out thread information / / Out: / - /----------------------------------------------------------------------------*/ DWORD WINAPI QueryThread(LPVOID pThreadParams) { HRESULT hresCoInit; MSG msg; LPTHREADINITDATA pThreadInitData = (LPTHREADINITDATA)pThreadParams; THREADDATA td;
ZeroMemory(&td, SIZEOF(td));
td.ptid = pThreadInitData; //td.cProperties = 0;
//td.aProperties = NULL;
//td.cColumns = 0;
//td.aColumnToPropertyMap = NULL;
hresCoInit = CoInitialize(NULL); FailGracefully(hresCoInit, "Failed to CoInitialize");
GetActiveWindow(); // ensure we have amessage queue
QueryThread_IssueQuery(&td);
while (GetMessage(&msg, NULL, 0, 0) > 0) { switch (msg.message) { case RVTM_STOPQUERY: TraceMsg("RVTM_STOPQUERY received - ignoring"); break;
case RVTM_REFRESH: { td.ptid->dwReference = (DWORD)msg.wParam; QueryThread_IssueQuery(&td); break; } case RVTM_SETCOLUMNTABLE: { if (td.ptid->hdsaColumns) DSA_DestroyCallback(td.ptid->hdsaColumns, FreeColumnCB, NULL);
td.ptid->dwReference = (DWORD)msg.wParam; td.ptid->hdsaColumns = (HDSA)msg.lParam;
QueryThread_FreePropertyList(&td); QueryThread_IssueQuery(&td); break; } default: break; } }
exit_gracefully:
QueryThread_FreePropertyList(&td); QueryThread_FreeThreadInitData(&td.ptid);
if (SUCCEEDED(hresCoInit)) CoUninitialize();
DllRelease(); ExitThread(0); return 0; // BOGUS: not never called
}
/*-----------------------------------------------------------------------------
/ QueryThread_FreeThreadInitData / ------------------------------ / Release the THREADINITDATA structure that we are given when the thread / is created. / / In: / pptid ->-> thread init data structure to be released / / Out: / - /----------------------------------------------------------------------------*/ VOID QueryThread_FreeThreadInitData(LPTHREADINITDATA* pptid) { LPTHREADINITDATA ptid = *pptid;
TraceEnter(TRACE_QUERYTHREAD, "QueryThread_FreeThreadInitData");
if (ptid) { LocalFreeStringW(&ptid->pQuery); LocalFreeStringW(&ptid->pScope);
if (ptid->hdsaColumns) DSA_DestroyCallback(ptid->hdsaColumns, FreeColumnCB, NULL);
LocalFreeStringW(&ptid->pServer); LocalFreeStringW(&ptid->pUserName); LocalFreeStringW(&ptid->pPassword);
LocalFree((HLOCAL)ptid); *pptid = NULL; }
TraceLeave(); }
/*-----------------------------------------------------------------------------
/ QueryThread_CheckForStopQuery / ----------------------------- / Peek the message queue looking for a stop query message, if we / can find one then we must bail out. / / In: / ptd -> thread data structure / / Out: / fStopQuery /----------------------------------------------------------------------------*/ BOOL QueryThread_CheckForStopQuery(LPTHREADDATA ptd) { BOOL fStopQuery = FALSE; MSG msg;
TraceEnter(TRACE_QUERYTHREAD, "QueryThread_CheckForStopQuery");
while (PeekMessage(&msg, NULL, RVTM_FIRST, RVTM_LAST, PM_REMOVE)) { TraceMsg("Found a RVTM_ message in queue, stopping query"); fStopQuery = TRUE; }
TraceLeaveValue(fStopQuery); }
/*-----------------------------------------------------------------------------
/ QueryThread_IssueQuery / ---------------------- / Issue a query using the IDirectorySearch interface, this is a more performant / to the wire interface that issues the query directly. The code binds to / the scope object (the specified path) and then issues the LDAP query / pumping the results into the viewer as required. / / In: / ptd -> thread information structurre / / Out: / HRESULT /----------------------------------------------------------------------------*/ HRESULT QueryThread_IssueQuery(LPTHREADDATA ptd) { HRESULT hr; DWORD dwres; LPTHREADINITDATA ptid = ptd->ptid; LPWSTR pQuery = NULL; INT cItems = 0, iColumn; INT cMaxResult = MAX_RESULT; BOOL fStopQuery = FALSE; IDirectorySearch* pDsSearch = NULL; LPWSTR pszTempPath = NULL; IDsDisplaySpecifier *pdds = NULL; ADS_SEARCH_HANDLE hSearch = NULL; ADS_SEARCHPREF_INFO prefInfo[3]; ADS_SEARCH_COLUMN column; HDPA hdpaResults = NULL; LPQUERYRESULT pResult = NULL; WCHAR szBuffer[2048]; // MAX_URL_LENGHT
INT resid; LPWSTR pColumnData = NULL; HKEY hkPolicy = NULL; USES_CONVERSION;
TraceEnter(TRACE_QUERYTHREAD, "QueryThread_IssueQuery");
// The foreground gave us a query so we are going to go and issue
// it now, having done this we will then be able to stream the
// result blobs back to the caller.
hr = QueryThread_GetFilter(&pQuery, ptid->pQuery, ptid->fShowHidden); FailGracefully(hr, "Failed to build LDAP query from scope, parameters + filter");
Trace(TEXT("Query is: %s"), W2T(pQuery)); Trace(TEXT("Scope is: %s"), W2T(ptid->pScope)); // Get the IDsDisplaySpecifier interface:
hr = CoCreateInstance(CLSID_DsDisplaySpecifier, NULL, CLSCTX_INPROC_SERVER, IID_IDsDisplaySpecifier, (void **)&pdds); FailGracefully(hr, "Failed to get the IDsDisplaySpecifier object");
hr = pdds->SetServer(ptid->pServer, ptid->pUserName, ptid->pPassword, DSSSF_DSAVAILABLE); FailGracefully(hr, "Failed to server information");
// initialize the query engine, specifying the scope, and the search parameters
hr = QueryThread_BuildPropertyList(ptd); FailGracefully(hr, "Failed to build property array to query for");
hr = ADsOpenObject(ptid->pScope, ptid->pUserName, ptid->pPassword, ADS_SECURE_AUTHENTICATION, IID_IDirectorySearch, (LPVOID*)&pDsSearch);
FailGracefully(hr, "Failed to get the IDirectorySearch interface for the given scope");
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_ASYNCHRONOUS; // async
prefInfo[1].vValue.dwType = ADSTYPE_BOOLEAN; prefInfo[1].vValue.Boolean = TRUE;
prefInfo[2].dwSearchPref = ADS_SEARCHPREF_PAGESIZE; // paged results
prefInfo[2].vValue.dwType = ADSTYPE_INTEGER; prefInfo[2].vValue.Integer = PAGE_SIZE;
hr = pDsSearch->SetSearchPreference(prefInfo, ARRAYSIZE(prefInfo)); FailGracefully(hr, "Failed to set search preferences");
hr = pDsSearch->ExecuteSearch(pQuery, ptd->aProperties, ptd->cProperties, &hSearch); FailGracefully(hr, "Failed in ExecuteSearch");
// pick up the policy value which defines the max results we are going to use
dwres = RegOpenKey(HKEY_CURRENT_USER, DS_POLICY, &hkPolicy); if (ERROR_SUCCESS == dwres) { DWORD dwType, cbSize;
dwres = RegQueryValueEx(hkPolicy, TEXT("QueryLimit"), NULL, &dwType, NULL, &cbSize); if ((ERROR_SUCCESS == dwres) && (dwType == REG_DWORD) && (cbSize == SIZEOF(cMaxResult))) { RegQueryValueEx(hkPolicy, TEXT("QueryLimit"), NULL, NULL, (LPBYTE)&cMaxResult, &cbSize); }
RegCloseKey(hkPolicy); }
// issue the query, pumping the results to the foreground UI which
// will inturn populate the the list view
Trace(TEXT("Result limit set to %d"), cMaxResult);
for (cItems = 0 ; cItems < cMaxResult; cItems++) { ADsSetLastError(ERROR_SUCCESS, NULL, NULL); // clear the ADSI previous errror
hr = pDsSearch->GetNextRow(hSearch);
fStopQuery = QueryThread_CheckForStopQuery(ptd); Trace(TEXT("fStopQuery %d, hr %08x"), fStopQuery, hr);
if (fStopQuery || (hr == S_ADS_NOMORE_ROWS)) { DWORD dwError; WCHAR wszError[64], wszName[64];
hr = ADsGetLastError(&dwError, wszError, ARRAYSIZE(wszError), wszName, ARRAYSIZE(wszName)); if (SUCCEEDED(hr) && (dwError != ERROR_MORE_DATA)) { break; }
hr = S_OK; // we have more data so lets continue
}
FailGracefully(hr, "Failed in GetNextRow");
// We have a result, lets ensure that we have posted the blob
// we are building before we start constructing a new one. We
// send pages of items to the fg thread for it to add to the
// result view, if the blob returns FALSE then we must tidy the
// DPA before continuing.
if (((cItems % 10) == 0) && hdpaResults) // 10 is a nice block size
{ TraceMsg("Posting results blob to fg thread"); if (!SEND_VIEW_MESSAGE(ptid, DSQVM_ADDRESULTS, (LPARAM)hdpaResults)) DPA_DestroyCallback(hdpaResults, FreeQueryResultCB, IntToPtr(ptd->cColumns));
hdpaResults = NULL; }
if (!hdpaResults) { hdpaResults = DPA_Create(PAGE_SIZE); TraceAssert(hdpaResults);
if (!hdpaResults) ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate result DPA"); }
// Add the result we have to the result blob, the first
// two things we need are the class and the ADsPath of the
// object, then loop over the properties to generate the
// column data
pResult = (LPQUERYRESULT)LocalAlloc(LPTR, SIZEOF(QUERYRESULT)+(SIZEOF(COLUMNVALUE)*(ptd->cColumns-1))); TraceAssert(pResult);
if (pResult) { // Get the ADsPath and ObjectClass of the object, these must remain UNICODE as
// they are used later for binding to the object. All other display information
// can be fixed up later.
pResult->iImage = -1;
// get the ADsPath. If the provider is GC: then replace with LDAP: so that
// when we interact with this object we are kept happy.
hr = pDsSearch->GetColumn(hSearch, c_szADsPath, &column); FailGracefully(hr, "Failed to get the ADsPath column");
hr = StringFromSearchColumn(&column, &pResult->pPath); pDsSearch->FreeColumn(&column);
Trace(TEXT("Object path: %s"), W2T(pResult->pPath));
if (SUCCEEDED(hr) && ((pResult->pPath[0]== L'G') && (pResult->pPath[1] == L'C'))) { TraceMsg("Replacing provider with LDAP:");
hr = LocalAllocStringLenW(&pszTempPath, lstrlenW(pResult->pPath)+2); if (SUCCEEDED(hr)) { StrCpyW(pszTempPath, c_szLDAP); StrCatW(pszTempPath, pResult->pPath+3); // skip GC:
LocalFreeStringW(&pResult->pPath); pResult->pPath = pszTempPath; }
Trace(TEXT("New path is: %s"), W2T(pResult->pPath)); }
FailGracefully(hr, "Failed to get ADsPath from column");
// get the objectClass
hr = pDsSearch->GetColumn(hSearch, c_szObjectClass, &column); FailGracefully(hr, "Failed to get the objectClass column");
hr = ObjectClassFromSearchColumn(&column, &pResult->pObjectClass); pDsSearch->FreeColumn(&column); FailGracefully(hr, "Failed to get object class from column");
// Now ensure that we have the icon cache, and then walk the list of columns
// getting the text that represents those.
if (SUCCEEDED(pdds->GetIconLocation(pResult->pObjectClass, DSGIF_GETDEFAULTICON, szBuffer, ARRAYSIZE(szBuffer), &resid))) { pResult->iImage = Shell_GetCachedImageIndex(W2T(szBuffer), resid, 0x0); Trace(TEXT("Image index from shell is: %d"), pResult->iImage); }
// is the class a container, mark this state into the result object
pResult->fIsContainer = pdds->IsClassContainer(pResult->pObjectClass, pResult->pPath, 0x0);
for (iColumn = 0 ; iColumn < ptd->cColumns ; iColumn++) { LPWSTR pProperty = ptd->aProperties[ptd->aColumnToPropertyMap[iColumn]]; TraceAssert(pProperty);
pResult->aColumn[iColumn].iPropertyType = PROPERTY_ISUNDEFINED; // empty column
hr = pDsSearch->GetColumn(hSearch, pProperty, &column); if ((hr != E_ADS_COLUMN_NOT_SET) && FAILED(hr)) { Trace(TEXT("Failed to get column %d with code %08x"), iColumn, hr); hr = E_ADS_COLUMN_NOT_SET; }
if (hr != E_ADS_COLUMN_NOT_SET) { LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(ptid->hdsaColumns, iColumn); TraceAssert(pColumn);
switch (pColumn->iPropertyType) { case PROPERTY_ISUNKNOWN: case PROPERTY_ISSTRING: { // we are treating the property as a string, therefore convert the search
// column to a string value and convert as required.
pResult->aColumn[iColumn].iPropertyType = PROPERTY_ISSTRING;
if (pColumn->fHasColumnHandler) { // we have the CLSID for a column handler, therefore lets CoCreate it
// and pass it to the ::GetText method.
if (!pColumn->pColumnHandler) { TraceGUID("Attempting to create IDsQueryColumnHandler from GUID: ", pColumn->clsidColumnHandler);
hr = CoCreateInstance(pColumn->clsidColumnHandler, NULL, CLSCTX_INPROC_SERVER, IID_IDsQueryColumnHandler, (LPVOID*)&pColumn->pColumnHandler); if (SUCCEEDED(hr)) hr = pColumn->pColumnHandler->Initialize(0x0, ptid->pServer, ptid->pUserName, ptid->pPassword);
if (FAILED(hr)) { TraceMsg("Failed to CoCreate the column handler, marking the column as not having one"); pColumn->fHasColumnHandler = FALSE; pColumn->pColumnHandler = NULL; } }
// if pColumnHandler != NULL, then call its ::GetText method, this is the string we should
// then place into the column.
if (pColumn->pColumnHandler) { pColumn->pColumnHandler->GetText(&column, szBuffer, ARRAYSIZE(szBuffer)); LocalAllocStringW2T(&pResult->aColumn[iColumn].pszText, szBuffer); } } else { // if we were able to convert the column value to a string,
// then lets pass it to the column handler (if there is one
// to get the display string), or just copy this into the column
// structure (thunking accordingly).
if (SUCCEEDED(StringFromSearchColumn(&column, &pColumnData))) { LocalAllocStringW2T(&pResult->aColumn[iColumn].pszText, pColumnData); LocalFreeStringW(&pColumnData); } }
break; } case PROPERTY_ISBOOL: // treat the BOOL as a number
case PROPERTY_ISNUMBER: { // its a number, therefore lets pick up the number value from the
// result and store that.
pResult->aColumn[iColumn].iPropertyType = PROPERTY_ISNUMBER; pResult->aColumn[iColumn].iValue = column.pADsValues->Integer; break; } }
pDsSearch->FreeColumn(&column); } }
if (-1 == DPA_AppendPtr(hdpaResults, pResult)) { FreeQueryResult(pResult, ptd->cColumns); LocalFree((HLOCAL)pResult); }
pResult = NULL; } }
hr = S_OK;
exit_gracefully:
Trace(TEXT("cItems %d, (hdpaResults %08x (%d))"), cItems, hdpaResults, hdpaResults ? DPA_GetPtrCount(hdpaResults):0);
if (hdpaResults) { // As we send bunches of results to the fg thread check to see if we have a
// DPA with any pending items in it, if we do then lets ensure we post that
// off, if that succedes (the msg returns TRUE) then we are done, otherwise
// hdpaResults needs to be free'd
Trace(TEXT("Posting remaining results to fg thread (%d)"), DPA_GetPtrCount(hdpaResults));
if (SEND_VIEW_MESSAGE(ptid, DSQVM_ADDRESULTS, (LPARAM)hdpaResults)) hdpaResults = NULL; }
if (!fStopQuery) { SEND_VIEW_MESSAGE(ptid, DSQVM_FINISHED, (cItems == MAX_RESULT)); }
if (pResult) { FreeQueryResult(pResult, ptd->cColumns); LocalFree((HLOCAL)pResult); }
if (hSearch && pDsSearch) { pDsSearch->CloseSearchHandle(hSearch); }
LocalFreeStringW(&pQuery);
DoRelease(pDsSearch); DoRelease(pdds);
QueryThread_FreePropertyList(ptd); // its void when we issue a new query
TraceLeaveResult(hr); }
/*-----------------------------------------------------------------------------
/ QueryThread_BuildPropertyList / ----------------------------- / Given the column DSA construct the property maps and the property / list we are going to query for. Internaly we always query for / ADsPath and objectClass, so walk the columns and work out / how many extra properties above this we have, then build an / array of property names containing the unique properties. / / We also construct an index table that maps from a column index / to a property name. / / In: / ptd -> thread information structurre / / Out: / HRESULT /----------------------------------------------------------------------------*/ HRESULT QueryThread_BuildPropertyList(LPTHREADDATA ptd) { HRESULT hr; LPTHREADINITDATA ptid = ptd->ptid; INT i, j; USES_CONVERSION;
TraceEnter(TRACE_QUERYTHREAD, "QueryThread_BuildPropertyList");
// Walk the list of columns and compute the properties that are unique to this
// query and generate a table for them. First count the property table
// based on the columns DSA
ptd->cProperties = PROPERTYMAP_USER; ptd->aProperties = NULL; ptd->cColumns = DSA_GetItemCount(ptid->hdsaColumns); ptd->aColumnToPropertyMap = NULL;
for (i = 0 ; i < DSA_GetItemCount(ptid->hdsaColumns); i++) { LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(ptid->hdsaColumns, i); TraceAssert(pColumn);
if (StrCmpW(pColumn->pProperty, c_szADsPath) && StrCmpW(pColumn->pProperty, c_szObjectClass)) { ptd->cProperties++; } } Trace(TEXT("cProperties %d"), ptd->cProperties); ptd->aProperties = (LPWSTR*)LocalAlloc(LPTR, SIZEOF(LPWSTR)*ptd->cProperties); ptd->aColumnToPropertyMap = (INT*)LocalAlloc(LPTR, SIZEOF(INT)*ptd->cColumns);
if (!ptd->aProperties || !ptd->aColumnToPropertyMap) ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate property array / display array"); ptd->aProperties[PROPERTYMAP_ADSPATH] = c_szADsPath; ptd->aProperties[PROPERTYMAP_OBJECTCLASS] = c_szObjectClass;
for (j = PROPERTYMAP_USER, i = 0 ; i < ptd->cColumns; i++) { LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(ptid->hdsaColumns, i); TraceAssert(pColumn);
if (!StrCmpW(pColumn->pProperty, c_szADsPath)) { ptd->aColumnToPropertyMap[i] = PROPERTYMAP_ADSPATH; } else if (!StrCmpW(pColumn->pProperty, c_szObjectClass)) { ptd->aColumnToPropertyMap[i] = PROPERTYMAP_OBJECTCLASS; } else { ptd->aProperties[j] = pColumn->pProperty; ptd->aColumnToPropertyMap[i] = j++; }
Trace(TEXT("Property: %s"), ptd->aProperties[ptd->aColumnToPropertyMap[i]]); }
hr = S_OK;
exit_gracefully:
if (FAILED(hr)) QueryThread_FreePropertyList(ptd);
TraceLeaveResult(hr); }
/*-----------------------------------------------------------------------------
/ QueryThread_FreePropertyList / ---------------------------- / Release a previously allocated property list assocaited with the / given thread. / / In: / ptd -> thread information structurre / / Out: / VOID /----------------------------------------------------------------------------*/ VOID QueryThread_FreePropertyList(LPTHREADDATA ptd) { TraceEnter(TRACE_QUERYTHREAD, "QueryThread_FreePropertyList");
if (ptd->aProperties) LocalFree((HLOCAL)ptd->aProperties); if (ptd->aColumnToPropertyMap) LocalFree((HLOCAL)ptd->aColumnToPropertyMap);
ptd->cProperties = 0; ptd->aProperties = NULL; ptd->cColumns = 0; ptd->aColumnToPropertyMap = NULL; TraceLeave(); }
/*-----------------------------------------------------------------------------
/ CQueryThreadCH / -------------- / Query thread column handler, this is a generic one used to convert / properties based on the CLSID we are instantiated with. /----------------------------------------------------------------------------*/
class CQueryThreadCH : public IDsQueryColumnHandler { private: LONG _cRef; CLSID _clsid; IADsPathname *_padp; IDsDisplaySpecifier *_pdds;
DWORD _dwFlags; LPWSTR _pszServer; LPWSTR _pszUserName; LPWSTR _pszPassword;
public: CQueryThreadCH(REFCLSID rCLSID); ~CQueryThreadCH();
// *** IUnknown ***
STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObject); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)();
// *** IDsQueryColumnHandler ***
STDMETHOD(Initialize)(THIS_ DWORD dwFlags, LPCWSTR pszServer, LPCWSTR pszUserName, LPCWSTR pszPassword); STDMETHOD(GetText)(ADS_SEARCH_COLUMN* psc, LPWSTR pszBuffer, INT cchBuffer); };
//
// constructor
//
CQueryThreadCH::CQueryThreadCH(REFCLSID rCLSID) : _cRef(1), _padp(NULL), _pdds(NULL), _clsid(rCLSID), _dwFlags(0), _pszServer(NULL), _pszUserName(NULL), _pszPassword(NULL) { TraceEnter(TRACE_QUERYTHREAD, "CQueryThreadCH::CQueryThreadCH"); TraceGUID("CLSID of property: ", rCLSID); DllAddRef(); TraceLeave(); }
CQueryThreadCH::~CQueryThreadCH() { TraceEnter(TRACE_QUERYTHREAD, "CQueryThreadCH::~CQueryThreadCH");
DoRelease(_padp); // free the name cracker
DoRelease(_pdds);
LocalFreeStringW(&_pszServer); LocalFreeStringW(&_pszUserName); LocalFreeStringW(&_pszPassword);
DllRelease();
TraceLeave(); }
//
// Handler IUnknown interface
//
ULONG CQueryThreadCH::AddRef() { return InterlockedIncrement(&_cRef); }
ULONG CQueryThreadCH::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
HRESULT CQueryThreadCH::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CQueryThreadCH, IDsQueryColumnHandler), // IID_IDsQueryColumnHandler
{0, 0 }, }; return QISearch(this, qit, riid, ppv); }
//
// Handle creating an instance of IDsFolderProperties for talking to WAB
//
STDAPI CQueryThreadCH_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi) { CQueryThreadCH *pqtch = new CQueryThreadCH(*poi->pclsid); if (!pqtch) return E_OUTOFMEMORY;
HRESULT hr = pqtch->QueryInterface(IID_IUnknown, (void **)ppunk); pqtch->Release(); return hr; }
/*-----------------------------------------------------------------------------
/ IDsQueryColumnHandler /----------------------------------------------------------------------------*/
STDMETHODIMP CQueryThreadCH::Initialize(THIS_ DWORD dwFlags, LPCWSTR pszServer, LPCWSTR pszUserName, LPCWSTR pszPassword) { TraceEnter(TRACE_QUERYTHREAD, "CQueryThread::Initialize");
LocalFreeStringW(&_pszServer); LocalFreeStringW(&_pszUserName); LocalFreeStringW(&_pszPassword);
// copy new parameters away
_dwFlags = dwFlags;
HRESULT hr = LocalAllocStringW(&_pszServer, pszServer); if (SUCCEEDED(hr)) hr = LocalAllocStringW(&_pszUserName, pszUserName); if (SUCCEEDED(hr)) hr = LocalAllocStringW(&_pszPassword, pszPassword);
DoRelease(_pdds) // discard previous IDisplaySpecifier object
TraceLeaveResult(hr); }
STDMETHODIMP CQueryThreadCH::GetText(ADS_SEARCH_COLUMN* psc, LPWSTR pszBuffer, INT cchBuffer) { HRESULT hr; LPWSTR pValue = NULL; USES_CONVERSION;
TraceEnter(TRACE_QUERYTHREAD, "CQueryThreadCH::GetText");
if (!psc || !pszBuffer) ExitGracefully(hr, E_UNEXPECTED, "Bad parameters passed to handler");
pszBuffer[0] = L'\0';
if (IsEqualCLSID(_clsid, CLSID_PublishedAtCH) || IsEqualCLSID(_clsid, CLSID_MachineOwnerCH)) { BOOL fPrefix = IsEqualCLSID(_clsid, CLSID_PublishedAtCH); LPCWSTR pszPath = psc->pADsValues[0].DNString; TraceAssert(pszPath != NULL);
// convert the ADsPath into its canonical form which is easier for the user
// to understand, CoCreate IADsPathname now instead of each time we call
// PrettyifyADsPathname.
if (!_padp) { hr = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_IADsPathname, (LPVOID*)&_padp); FailGracefully(hr, "Failed to get IADsPathname interface"); }
if (FAILED(GetDisplayNameFromADsPath(pszPath, pszBuffer, cchBuffer, _padp, fPrefix))) { TraceMsg("Failed to get display name from path"); StrCpyNW(pszBuffer, pszPath, cchBuffer); } hr = S_OK; } else if (IsEqualCLSID(_clsid, CLSID_ObjectClassCH)) { // get a string from the search column, and then look up the friendly name of the
// class from its display specifier
hr = ObjectClassFromSearchColumn(psc, &pValue); FailGracefully(hr, "Failed to get object class from psc");
if (!_pdds) { DWORD dwFlags = 0;
hr = CoCreateInstance(CLSID_DsDisplaySpecifier, NULL, CLSCTX_INPROC_SERVER, IID_IDsDisplaySpecifier, (void **)&_pdds); FailGracefully(hr, "Failed to get IDsDisplaySpecifier interface");
hr = _pdds->SetServer(_pszServer, _pszUserName, _pszPassword, DSSSF_DSAVAILABLE); FailGracefully(hr, "Failed when setting server for display specifier object"); }
_pdds->GetFriendlyClassName(pValue, pszBuffer, cchBuffer); } else if (IsEqualCLSID(_clsid, CLSID_MachineOwnerCH)) { // convert the DN of the user object into a string that we can display
} else if (IsEqualCLSID(_clsid, CLSID_MachineRoleCH)) { // convert the userAccountControl value into something we can display for the user
if (psc->dwADsType == ADSTYPE_INTEGER) { INT iType = psc->pADsValues->Integer; // pick out the type
if ((iType >= 4096) && (iType <= 8191)) { TraceMsg("Returning WKS/SRV string"); LoadStringW(GLOBAL_HINSTANCE, IDS_WKSORSERVER, pszBuffer, cchBuffer); } else if (iType >= 8192) { TraceMsg("Returning DC string"); LoadStringW(GLOBAL_HINSTANCE, IDS_DC, pszBuffer, cchBuffer); } else { Trace(TEXT("Unknown type %x"), iType); } } } else { ExitGracefully(hr, E_UNEXPECTED, "m_clsid specifies column type not supported"); }
hr = S_OK;
exit_gracefully:
LocalFreeStringW(&pValue);
TraceLeaveResult(hr); }
|