windows-nt/Source/XPSP1/NT/shell/ext/dsui/dsquery/thread.cpp
2020-09-26 16:20:57 +08:00

1017 lines
33 KiB
C++

#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);
}