thread.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 33k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. #include "pch.h"
  2. #include "stddef.h"
  3. #pragma hdrstop
  4. /*-----------------------------------------------------------------------------
  5. / Query thread bits
  6. /----------------------------------------------------------------------------*/
  7. //
  8. // Page size used for paging the result sets (the LDAP server core is a sync process)
  9. // therefore getting it to return smaller result blobs is better for us
  10. //
  11. #define PAGE_SIZE                   64
  12. #define MAX_RESULT                  10000
  13. //
  14. // When the query is issued we always pull back at least ADsPath and objectClass
  15. // as properites (these are required for the viewer to work).  Therefore these define
  16. // the mapping of these values to the returned column set.
  17. //
  18. #define PROPERTYMAP_ADSPATH         0
  19. #define PROPERTYMAP_OBJECTCLASS     1
  20. #define PROPERTYMAP_USER            2
  21. #define INDEX_TO_PROPERTY(i)        (i+PROPERTYMAP_USER)
  22. //
  23. // THREADDATA this is the state structure for the thread, it encapsulates
  24. // the parameters and other junk required to the keep the thread alive.
  25. //
  26. typedef struct
  27. {
  28.     LPTHREADINITDATA ptid;    
  29.     INT              cProperties;            // number of properties in aProperties
  30.     LPWSTR*          aProperties;            // array of string pointers for "property names"
  31.     INT              cColumns;               // number of columsn in view
  32.     INT*             aColumnToPropertyMap;   // mapping from display column index to property name
  33. } THREADDATA, * LPTHREADDATA;
  34. //
  35. // Helper macro to send messages for the fg view, including the reference
  36. // count
  37. // 
  38. #define SEND_VIEW_MESSAGE(ptid, uMsg, lParam) 
  39.         SendMessage(GetParent(ptid->hwndView), uMsg, (ptid)->dwReference, lParam)
  40. //
  41. // Function prototypes for the query thread engine.
  42. //
  43. HRESULT QueryThread_IssueQuery(LPTHREADDATA ptd);
  44. HRESULT QueryThread_BuildPropertyList(LPTHREADDATA ptd);
  45. VOID QueryThread_FreePropertyList(LPTHREADDATA ptd);
  46. /*-----------------------------------------------------------------------------
  47. / Helper functions
  48. /----------------------------------------------------------------------------*/
  49. /*-----------------------------------------------------------------------------
  50. / QueryThread_GetFilter
  51. / ----------------------
  52. /   Construct the LDAP filter we are going to use for this query.
  53. /
  54. / In:
  55. /   ppQuery -> receives the full filter
  56. /   pBaseFilter -> filter string to use as base
  57. /   fShowHidden = show hidden objects?
  58. /
  59. / Out:
  60. /   HRESULT
  61. /----------------------------------------------------------------------------*/
  62. VOID _GetFilter(LPWSTR pFilter, UINT* pcchFilterLen, LPWSTR pBaseFilter, BOOL fShowHidden)
  63. {
  64.     HRESULT hres;
  65.     TraceEnter(TRACE_QUERYTHREAD, "_GetFilter");
  66.     if ( pFilter )
  67.         *pFilter = TEXT('');
  68.     PutStringElementW(pFilter, pcchFilterLen, L"(&");
  69.     if ( !fShowHidden )
  70.         PutStringElementW(pFilter, pcchFilterLen, c_szShowInAdvancedViewOnly);
  71.     PutStringElementW(pFilter, pcchFilterLen, pBaseFilter);
  72.     PutStringElementW(pFilter, pcchFilterLen, L")");
  73.     TraceLeave();
  74. }
  75. HRESULT QueryThread_GetFilter(LPWSTR* ppFilter, LPWSTR pBaseFilter, BOOL fShowHidden)
  76. {
  77.     HRESULT hres;
  78.     UINT cchFilterLen = 0;
  79.     TraceEnter(TRACE_QUERYTHREAD, "QueryThread_GetFilter");
  80.      _GetFilter(NULL, &cchFilterLen, pBaseFilter, fShowHidden);
  81.     hres = LocalAllocStringLenW(ppFilter, cchFilterLen);
  82.     FailGracefully(hres, "Failed to allocate buffer for query string");
  83.     
  84.     _GetFilter(*ppFilter, NULL, pBaseFilter, fShowHidden);
  85.     
  86.     hres = S_OK;
  87. exit_gracefully:
  88.    
  89.     TraceLeaveResult(hres);
  90. }
  91. /*-----------------------------------------------------------------------------
  92. / Background query thread, this does the work of issuing the query and then
  93. / populating the view.
  94. /----------------------------------------------------------------------------*/
  95. /*-----------------------------------------------------------------------------
  96. / QueryThread
  97. / -----------
  98. /   Thread function sits spinning its wheels waiting for a query to be
  99. /   received from the outside world.  The main result viewer communicates
  100. /   with this code by ThreadSendMessage.
  101. /
  102. / In:
  103. /   pThreadParams -> structure that defines out thread information
  104. /
  105. / Out:
  106. /   -
  107. /----------------------------------------------------------------------------*/
  108. DWORD WINAPI QueryThread(LPVOID pThreadParams)
  109. {
  110.     HRESULT hresCoInit;
  111.     MSG msg;
  112.     LPTHREADINITDATA pThreadInitData = (LPTHREADINITDATA)pThreadParams;
  113.     THREADDATA td;
  114.     ZeroMemory(&td, SIZEOF(td));
  115.     td.ptid = pThreadInitData;
  116.     //td.cProperties = 0;
  117.     //td.aProperties = NULL;
  118.     //td.cColumns = 0;
  119.     //td.aColumnToPropertyMap = NULL;
  120.     
  121.     hresCoInit = CoInitialize(NULL);
  122.     FailGracefully(hresCoInit, "Failed to CoInitialize");
  123.     GetActiveWindow();                                      // ensure we have amessage queue
  124.     QueryThread_IssueQuery(&td);
  125.     while ( GetMessage(&msg, NULL, 0, 0) > 0 )
  126.     {
  127.         switch ( msg.message )
  128.         {
  129.             case RVTM_STOPQUERY:
  130.                 TraceMsg("RVTM_STOPQUERY received - ignoring");
  131.                 break;
  132.             case RVTM_REFRESH:
  133.             {
  134.                 td.ptid->dwReference = (DWORD)msg.wParam;
  135.                 QueryThread_IssueQuery(&td);
  136.                 break;
  137.             }
  138.             
  139.             case RVTM_SETCOLUMNTABLE:
  140.             {
  141.                 if ( td.ptid->hdsaColumns )
  142.                     DSA_DestroyCallback(td.ptid->hdsaColumns, FreeColumnCB, NULL);
  143.                 td.ptid->dwReference = (DWORD)msg.wParam;
  144.                 td.ptid->hdsaColumns = (HDSA)msg.lParam;        
  145.                 QueryThread_FreePropertyList(&td);
  146.                 QueryThread_IssueQuery(&td);
  147.                 break;
  148.             }
  149.                                           
  150.             default:
  151.                 break;
  152.         }
  153.     }
  154. exit_gracefully:
  155.     QueryThread_FreePropertyList(&td);
  156.     QueryThread_FreeThreadInitData(&td.ptid);
  157.     if ( SUCCEEDED(hresCoInit) )
  158.         CoUninitialize();
  159.     InterlockedIncrement(&GLOBAL_REFCOUNT);
  160.     ExitThread(0);
  161.     return 0;               // BOGUS: not never called
  162. }
  163. /*-----------------------------------------------------------------------------
  164. / QueryThread_FreeThreadInitData
  165. / ------------------------------
  166. /   Release the THREADINITDATA structure that we are given when the thread
  167. /   is created.
  168. /
  169. / In:
  170. /   pptid ->-> thread init data structure to be released 
  171. /
  172. / Out:
  173. /   -
  174. /----------------------------------------------------------------------------*/
  175. VOID QueryThread_FreeThreadInitData(LPTHREADINITDATA* pptid)
  176. {
  177.     LPTHREADINITDATA ptid = *pptid;
  178.     TraceEnter(TRACE_QUERYTHREAD, "QueryThread_FreeThreadInitData");
  179.     if ( ptid )
  180.     {
  181.         LocalFreeStringW(&ptid->pQuery);
  182.         LocalFreeStringW(&ptid->pScope);
  183.         if ( ptid->hdsaColumns )
  184.             DSA_DestroyCallback(ptid->hdsaColumns, FreeColumnCB, NULL);
  185.         LocalFreeStringW(&ptid->pServer);
  186.         LocalFreeStringW(&ptid->pUserName);
  187.         LocalFreeStringW(&ptid->pPassword);
  188.         LocalFree((HLOCAL)ptid);
  189.         *pptid = NULL;
  190.     }
  191.     TraceLeave();
  192. }
  193. /*-----------------------------------------------------------------------------
  194. / QueryThread_CheckForStopQuery
  195. / -----------------------------
  196. /   Peek the message queue looking for a stop query message, if we
  197. /   can find one then we must bail out.
  198. /
  199. / In:
  200. /   ptd -> thread data structure
  201. /
  202. / Out:
  203. /   fStopQuery
  204. /----------------------------------------------------------------------------*/
  205. BOOL QueryThread_CheckForStopQuery(LPTHREADDATA ptd)
  206. {
  207.     BOOL fStopQuery = FALSE;
  208.     MSG msg;
  209.     TraceEnter(TRACE_QUERYTHREAD, "QueryThread_CheckForStopQuery");
  210.     while ( PeekMessage(&msg, NULL, RVTM_FIRST, RVTM_LAST, PM_REMOVE) )
  211.     {
  212.         TraceMsg("Found a RVTM_ message in queue, stopping query");
  213.         fStopQuery = TRUE;
  214.     }
  215.     TraceLeaveValue(fStopQuery);
  216. }
  217. /*-----------------------------------------------------------------------------
  218. / QueryThread_IssueQuery
  219. / ----------------------
  220. /   Issue a query using the IDirectorySearch interface, this is a more performant
  221. /   to the wire interface that issues the query directly.   The code binds to 
  222. /   the scope object (the specified path) and then issues the LDAP query
  223. /   pumping the results into the viewer as required.
  224. /
  225. / In:
  226. /   ptd -> thread information structurre
  227. /
  228. / Out:
  229. /   HRESULT
  230. /----------------------------------------------------------------------------*/
  231. HRESULT QueryThread_IssueQuery(LPTHREADDATA ptd)
  232. {
  233.     HRESULT hres;
  234.     DWORD dwres;
  235.     LPTHREADINITDATA ptid = ptd->ptid;
  236.     LPWSTR pQuery = NULL;
  237.     INT cItems, iColumn;
  238.     INT cMaxResult = MAX_RESULT;
  239.     BOOL fStopQuery = FALSE;
  240.     IDirectorySearch* pDsSearch = NULL;
  241.     LPWSTR pszTempPath = NULL;
  242.     IDsDisplaySpecifier *pdds = NULL;
  243.     ADS_SEARCH_HANDLE hSearch = NULL;
  244.     ADS_SEARCHPREF_INFO prefInfo[3];
  245.     ADS_SEARCH_COLUMN column;
  246.     HDPA hdpaResults = NULL;
  247.     LPQUERYRESULT pResult = NULL;
  248.     WCHAR szBuffer[2048];               // MAX_URL_LENGHT
  249.     INT resid;
  250.     LPWSTR pColumnData = NULL;
  251.     HKEY hkPolicy = NULL;
  252.     USES_CONVERSION;
  253.     TraceEnter(TRACE_QUERYTHREAD, "QueryThread_IssueQuery");    
  254.     // The foreground gave us a query so we are going to go and issue
  255.     // it now, having done this we will then be able to stream the 
  256.     // result blobs back to the caller. 
  257.     hres = QueryThread_GetFilter(&pQuery, ptid->pQuery, ptid->fShowHidden);
  258.     FailGracefully(hres, "Failed to build LDAP query from scope, parameters + filter");
  259.     Trace(TEXT("Query is: %s"), W2T(pQuery));
  260.     Trace(TEXT("Scope is: %s"), W2T(ptid->pScope));
  261.     
  262.     // Get the IDsDisplaySpecifier interface:
  263.     hres = CoCreateInstance(CLSID_DsDisplaySpecifier, NULL, CLSCTX_INPROC_SERVER, IID_IDsDisplaySpecifier, (void **)&pdds);
  264.     FailGracefully(hres, "Failed to get the IDsDisplaySpecifier object");
  265.     hres = pdds->SetServer(ptid->pServer, ptid->pUserName, ptid->pPassword, DSSSF_DSAVAILABLE);
  266.     FailGracefully(hres, "Failed to server information");
  267.     // initialize the query engine, specifying the scope, and the search parameters
  268.     hres = QueryThread_BuildPropertyList(ptd);
  269.     FailGracefully(hres, "Failed to build property array to query for");
  270.     hres = ADsOpenObject(ptid->pScope, ptid->pUserName, ptid->pPassword, ADS_SECURE_AUTHENTICATION,
  271.                             IID_IDirectorySearch, (LPVOID*)&pDsSearch);
  272.     FailGracefully(hres, "Failed to get the IDirectorySearch interface for the given scope");
  273.     prefInfo[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;     // sub-tree search
  274.     prefInfo[0].vValue.dwType = ADSTYPE_INTEGER;
  275.     prefInfo[0].vValue.Integer = ADS_SCOPE_SUBTREE;
  276.     prefInfo[1].dwSearchPref = ADS_SEARCHPREF_ASYNCHRONOUS;     // async
  277.     prefInfo[1].vValue.dwType = ADSTYPE_BOOLEAN;
  278.     prefInfo[1].vValue.Boolean = TRUE;
  279.     prefInfo[2].dwSearchPref = ADS_SEARCHPREF_PAGESIZE;         // paged results
  280.     prefInfo[2].vValue.dwType = ADSTYPE_INTEGER;
  281.     prefInfo[2].vValue.Integer = PAGE_SIZE;
  282.     hres = pDsSearch->SetSearchPreference(prefInfo, ARRAYSIZE(prefInfo));
  283.     FailGracefully(hres, "Failed to set search preferences");
  284.     hres = pDsSearch->ExecuteSearch(pQuery, ptd->aProperties, ptd->cProperties, &hSearch);
  285.     FailGracefully(hres, "Failed in ExecuteSearch");
  286.     // pick up the policy value which defines the max results we are going to use
  287.     dwres = RegOpenKey(HKEY_CURRENT_USER, DS_POLICY, &hkPolicy);
  288.     if ( ERROR_SUCCESS == dwres )
  289.     {
  290.         DWORD dwType, cbSize;
  291.         dwres = RegQueryValueEx(hkPolicy, TEXT("QueryLimit"), NULL, &dwType, NULL, &cbSize);
  292.         if ( (ERROR_SUCCESS == dwres) && (dwType == REG_DWORD) && (cbSize == SIZEOF(cMaxResult)) )
  293.         {
  294.             RegQueryValueEx(hkPolicy, TEXT("QueryLimit"), NULL, NULL, (LPBYTE)&cMaxResult, &cbSize);
  295.         }
  296.         RegCloseKey(hkPolicy);
  297.     }
  298.     
  299.     // issue the query, pumping the results to the foreground UI which
  300.     // will inturn populate the the list view
  301.     Trace(TEXT("Result limit set to %d"), cMaxResult);
  302.     for ( cItems = 0 ; cItems < cMaxResult; cItems++ )
  303.     {
  304.         hres = pDsSearch->GetNextRow(hSearch);
  305.         fStopQuery = QueryThread_CheckForStopQuery(ptd);
  306.         
  307.         Trace(TEXT("fStopQuery %d, hr %08x"), fStopQuery, hres);
  308.         if ( fStopQuery || (hres == S_ADS_NOMORE_ROWS) )
  309.             break;
  310.         FailGracefully(hres, "Failed in GetNextRow");
  311.         // We have a result, lets ensure that we have posted the blob
  312.         // we are building before we start constructing a new one.  We
  313.         // send pages of items to the fg thread for it to add to the
  314.         // result view, if the blob returns FALSE then we must tidy the
  315.         // DPA before continuing.
  316.         
  317.         if ( ((cItems % 10) == 0) && hdpaResults )          // 10 is a nice block size
  318.         {
  319.             TraceMsg("Posting results blob to fg thread");
  320.             
  321.             if ( !SEND_VIEW_MESSAGE(ptid, DSQVM_ADDRESULTS, (LPARAM)hdpaResults) )
  322.                 DPA_DestroyCallback(hdpaResults, FreeQueryResultCB, (LPVOID)ptd->cColumns);
  323.             hdpaResults = NULL;
  324.         }
  325.         if ( !hdpaResults )
  326.         {
  327.             hdpaResults = DPA_Create(PAGE_SIZE);
  328.             TraceAssert(hdpaResults);
  329.             if ( !hdpaResults )
  330.                 ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate result DPA");
  331.         }
  332.         // Add the result we have to the result blob, the first
  333.         // two things we need are the class and the ADsPath of the
  334.         // object, then loop over the properties to generate the
  335.         // column data
  336.         pResult = (LPQUERYRESULT)LocalAlloc(LPTR, SIZEOF(QUERYRESULT)+(SIZEOF(COLUMNVALUE)*(ptd->cColumns-1)));
  337.         TraceAssert(pResult);
  338.         if ( pResult )
  339.         {
  340.             // Get the ADsPath and ObjectClass of the object, these must remain UNICODE as
  341.             // they are used later for binding to the object.  All other display information
  342.             // can be fixed up later.
  343.             pResult->iImage = -1;
  344.             // get the ADsPath.  If the provider is GC: then replace with LDAP: so that
  345.             // when we interact with this object we are kept happy.
  346.             hres = pDsSearch->GetColumn(hSearch, c_szADsPath, &column);
  347.             FailGracefully(hres, "Failed to get the ADsPath column");
  348.             hres = StringFromSearchColumn(&column, &pResult->pPath);
  349.             pDsSearch->FreeColumn(&column);
  350.             Trace(TEXT("Object path: %s"), W2T(pResult->pPath));
  351.             if ( SUCCEEDED(hres) &&
  352.                     ((pResult->pPath[0]== L'G') && (pResult->pPath[1] == L'C')) )
  353.             {
  354.                 TraceMsg("Replacing provider with LDAP:");
  355.                 hres = LocalAllocStringLenW(&pszTempPath, lstrlenW(pResult->pPath)+2);
  356.                 if ( SUCCEEDED(hres) )
  357.                 {
  358.                     StrCpyW(pszTempPath, c_szLDAP);
  359.                     StrCatW(pszTempPath, pResult->pPath+3);           // skip GC:
  360.                     LocalFreeStringW(&pResult->pPath);
  361.                     pResult->pPath = pszTempPath;
  362.                 }
  363.                 Trace(TEXT("New path is: %s"), W2T(pResult->pPath));
  364.             }
  365.             FailGracefully(hres, "Failed to get ADsPath from column");
  366.             // get the objectClass
  367.             hres = pDsSearch->GetColumn(hSearch, c_szObjectClass, &column);
  368.             FailGracefully(hres, "Failed to get the objectClass column");
  369.             hres = ObjectClassFromSearchColumn(&column, &pResult->pObjectClass);
  370.             pDsSearch->FreeColumn(&column);
  371.             FailGracefully(hres, "Failed to get object class from column");
  372.             // Now ensure that we have the icon cache, and then walk the list of columns
  373.             // getting the text that represents those.
  374.             if ( SUCCEEDED(pdds->GetIconLocation(pResult->pObjectClass, DSGIF_GETDEFAULTICON, szBuffer, ARRAYSIZE(szBuffer), &resid)) )
  375.             {
  376.                 pResult->iImage = Shell_GetCachedImageIndex(W2T(szBuffer), resid, 0x0);
  377.                 Trace(TEXT("Image index from shell is: %d"), pResult->iImage);
  378.             }
  379.             for ( iColumn = 0 ; iColumn < ptd->cColumns ; iColumn++ )
  380.             {
  381.                 LPWSTR pProperty = ptd->aProperties[ptd->aColumnToPropertyMap[iColumn]];    
  382.                 TraceAssert(pProperty);
  383.                 pResult->aColumn[iColumn].iPropertyType = PROPERTY_ISUNDEFINED;     // empty column
  384.                 hres = pDsSearch->GetColumn(hSearch, pProperty, &column);
  385.                 if ( (hres != E_ADS_COLUMN_NOT_SET) && FAILED(hres) )
  386.                 {
  387.                     Trace(TEXT("Failed to get column %d with code %08x"), iColumn, hres);
  388.                     hres = E_ADS_COLUMN_NOT_SET;
  389.                 }
  390.                 if ( hres != E_ADS_COLUMN_NOT_SET )
  391.                 {
  392.                     LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(ptid->hdsaColumns, iColumn);
  393.                     TraceAssert(pColumn);
  394.                     switch ( pColumn->iPropertyType )
  395.                     {
  396.                         case PROPERTY_ISUNKNOWN:
  397.                         case PROPERTY_ISSTRING:
  398.                         {                            
  399.                             // we are treating the property as a string, therefore convert the search
  400.                             // column to a string value and convert as required.
  401.                             pResult->aColumn[iColumn].iPropertyType = PROPERTY_ISSTRING;
  402.                             if ( pColumn->fHasColumnHandler )
  403.                             {
  404.                                 // we have the CLSID for a column handler, therefore lets CoCreate it
  405.                                 // and pass it to the ::GetText method.
  406.                                 if ( !pColumn->pColumnHandler )
  407.                                 {
  408.                                     TraceGUID("Attempting to create IDsQueryColumnHandler from GUID: ", pColumn->clsidColumnHandler);
  409.                                     hres = CoCreateInstance(pColumn->clsidColumnHandler, NULL, CLSCTX_INPROC_SERVER, 
  410.                                                                 IID_IDsQueryColumnHandler, (LPVOID*)&pColumn->pColumnHandler);
  411.                                     if ( SUCCEEDED(hres) )
  412.                                         hres = pColumn->pColumnHandler->Initialize(0x0, ptid->pServer, ptid->pUserName, ptid->pPassword);
  413.                                     if ( FAILED(hres) )
  414.                                     {
  415.                                         TraceMsg("Failed to CoCreate the column handler, marking the column as not having one");
  416.                                         pColumn->fHasColumnHandler = FALSE;
  417.                                         pColumn->pColumnHandler = NULL;
  418.                                     }
  419.                                 }                                        
  420.                                 // if pColumnHandler != NULL, then call its ::GetText method, this is the string we should
  421.                                 // then place into the column.
  422.                                 if ( pColumn->pColumnHandler )
  423.                                 {
  424.                                     pColumn->pColumnHandler->GetText(&column, szBuffer, ARRAYSIZE(szBuffer));
  425.                                     LocalAllocStringW2T(&pResult->aColumn[iColumn].pszText, szBuffer);
  426.                                 }
  427.                             }
  428.                             else
  429.                             {
  430.                                 // if we were able to convert the column value to a string,
  431.                                 // then lets pass it to the column handler (if there is one
  432.                                 // to get the display string), or just copy this into the column
  433.                                 // structure (thunking accordingly).
  434.                         
  435.                                 if ( SUCCEEDED(StringFromSearchColumn(&column, &pColumnData)) )
  436.                                 {
  437.                                     LocalAllocStringW2T(&pResult->aColumn[iColumn].pszText, pColumnData);
  438.                                     LocalFreeStringW(&pColumnData);
  439.                                 }
  440.                             }
  441.                             break;
  442.                         }
  443.                         
  444.                         case PROPERTY_ISBOOL:                                   // treat the BOOL as a number
  445.                         case PROPERTY_ISNUMBER:
  446.                         {
  447.                             // its a number, therefore lets pick up the number value from the
  448.                             // result and store that.
  449.                             pResult->aColumn[iColumn].iPropertyType = PROPERTY_ISNUMBER;
  450.                             pResult->aColumn[iColumn].iValue = column.pADsValues->Integer;
  451.                             break;
  452.                         }
  453.                     }
  454.                     pDsSearch->FreeColumn(&column);
  455.                 }
  456.             }        
  457.             if ( -1 == DPA_AppendPtr(hdpaResults, pResult) )
  458.             {
  459.                 FreeQueryResult(pResult, ptd->cColumns);
  460.                 LocalFree((HLOCAL)pResult);
  461.             }
  462.             pResult = NULL;
  463.         }
  464.     }
  465.     hres = S_OK;
  466. exit_gracefully:
  467.     Trace(TEXT("cItems %d, (hdpaResults %08x (%d))"), cItems, hdpaResults, hdpaResults ? DPA_GetPtrCount(hdpaResults):0);
  468.     if ( hdpaResults )
  469.     {
  470.         // As we send bunches of results to the fg thread check to see if we have a 
  471.         // DPA with any pending items in it, if we do then lets ensure we post that
  472.         // off, if that succedes (the msg returns TRUE) then we are done, otherwise
  473.         // hdpaResults needs to be free'd
  474.         Trace(TEXT("Posting remaining results to fg thread (%d)"), DPA_GetPtrCount(hdpaResults));
  475.         if ( SEND_VIEW_MESSAGE(ptid, DSQVM_ADDRESULTS, (LPARAM)hdpaResults) )
  476.             hdpaResults = NULL;
  477.     }
  478.     if ( !fStopQuery )
  479.     {
  480.         SEND_VIEW_MESSAGE(ptid, DSQVM_FINISHED, (cItems == MAX_RESULT));
  481.     }
  482.     if ( pResult )
  483.     {
  484.         FreeQueryResult(pResult, ptd->cColumns);
  485.         LocalFree((HLOCAL)pResult);
  486.     }
  487.     if ( hSearch && pDsSearch )
  488.     {
  489.         pDsSearch->CloseSearchHandle(hSearch);
  490.     }
  491.     LocalFreeStringW(&pQuery);
  492.     DoRelease(pDsSearch);
  493.     DoRelease(pdds);
  494.     QueryThread_FreePropertyList(ptd);               // its void when we issue a new query
  495.     TraceLeaveResult(hres);
  496. }
  497. /*-----------------------------------------------------------------------------
  498. / QueryThread_BuildPropertyList
  499. / -----------------------------
  500. /   Given the column DSA construct the property maps and the property
  501. /   list we are going to query for.  Internaly we always query for
  502. /   ADsPath and objectClass, so walk the columns and work out
  503. /   how many extra properties above this we have, then build an
  504. /   array of property names containing the unique properties.
  505. /
  506. /   We also construct an index table that maps from a column index
  507. /   to a property name.
  508. /
  509. / In:
  510. /   ptd -> thread information structurre
  511. /
  512. / Out:
  513. /   HRESULT
  514. /----------------------------------------------------------------------------*/
  515. HRESULT QueryThread_BuildPropertyList(LPTHREADDATA ptd)
  516. {
  517.     HRESULT hres;
  518.     LPTHREADINITDATA ptid = ptd->ptid;
  519.     INT i, j;
  520.     USES_CONVERSION;
  521.     TraceEnter(TRACE_QUERYTHREAD, "QueryThread_BuildPropertyList");
  522.     // Walk the list of columns and compute the properties that are unique to this
  523.     // query and generate a table for them.  First count the property table
  524.     // based on the columns DSA
  525.     ptd->cProperties = PROPERTYMAP_USER;
  526.     ptd->aProperties = NULL;
  527.     ptd->cColumns = DSA_GetItemCount(ptid->hdsaColumns);
  528.     ptd->aColumnToPropertyMap = NULL;
  529.     for ( i = 0 ; i < DSA_GetItemCount(ptid->hdsaColumns); i++ )
  530.     {
  531.         LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(ptid->hdsaColumns, i);
  532.         TraceAssert(pColumn);
  533.         if ( StrCmpW(pColumn->pProperty, c_szADsPath) &&
  534.                  StrCmpW(pColumn->pProperty, c_szObjectClass) )
  535.         {
  536.            ptd->cProperties++;
  537.         }
  538.     }
  539.        
  540.     Trace(TEXT("cProperties %d"), ptd->cProperties);
  541.         
  542.     ptd->aProperties = (LPWSTR*)LocalAlloc(LPTR, SIZEOF(LPWSTR)*ptd->cProperties);
  543.     ptd->aColumnToPropertyMap = (INT*)LocalAlloc(LPTR, SIZEOF(INT)*ptd->cColumns);
  544.     if ( !ptd->aProperties || !ptd->aColumnToPropertyMap )
  545.         ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate property array / display array");
  546.     
  547.     ptd->aProperties[PROPERTYMAP_ADSPATH] = c_szADsPath;
  548.     ptd->aProperties[PROPERTYMAP_OBJECTCLASS] = c_szObjectClass;
  549.     for ( j = PROPERTYMAP_USER, i = 0 ; i < ptd->cColumns; i++ )
  550.     {
  551.         LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(ptid->hdsaColumns, i);
  552.         TraceAssert(pColumn);
  553.         if ( !StrCmpW(pColumn->pProperty, c_szADsPath) )
  554.         {
  555.            ptd->aColumnToPropertyMap[i] = PROPERTYMAP_ADSPATH;
  556.         }
  557.         else if ( !StrCmpW(pColumn->pProperty, c_szObjectClass) )
  558.         {
  559.            ptd->aColumnToPropertyMap[i] = PROPERTYMAP_OBJECTCLASS;
  560.         }
  561.         else
  562.         {
  563.             ptd->aProperties[j] = pColumn->pProperty;
  564.             ptd->aColumnToPropertyMap[i] = j++;
  565.         }
  566.         Trace(TEXT("Property: %s"), ptd->aProperties[ptd->aColumnToPropertyMap[i]]);
  567.     }
  568.     hres = S_OK;
  569. exit_gracefully:
  570.     if ( FAILED(hres) )
  571.         QueryThread_FreePropertyList(ptd);
  572.     TraceLeaveResult(hres);
  573. }
  574. /*-----------------------------------------------------------------------------
  575. / QueryThread_FreePropertyList
  576. / ----------------------------
  577. /   Release a previously allocated property list assocaited with the
  578. /   given thread.
  579. /
  580. / In:
  581. /   ptd -> thread information structurre
  582. /
  583. / Out:
  584. /   VOID
  585. /----------------------------------------------------------------------------*/
  586. VOID QueryThread_FreePropertyList(LPTHREADDATA ptd)
  587. {
  588.     TraceEnter(TRACE_QUERYTHREAD, "QueryThread_FreePropertyList");
  589.     if ( ptd->aProperties )
  590.         LocalFree((HLOCAL)ptd->aProperties);
  591.     if ( ptd->aColumnToPropertyMap )
  592.         LocalFree((HLOCAL)ptd->aColumnToPropertyMap);
  593.     ptd->cProperties = 0;    
  594.     ptd->aProperties = NULL;
  595.     ptd->cColumns = 0;
  596.     ptd->aColumnToPropertyMap = NULL;
  597.     
  598.     TraceLeave();
  599. }
  600. /*-----------------------------------------------------------------------------
  601. / CQueryThreadCH
  602. / --------------
  603. /   Query thread column handler, this is a generic one used to convert
  604. /   properties based on the CLSID we are instantiated with.
  605. /----------------------------------------------------------------------------*/
  606. class CQueryThreadCH : public IDsQueryColumnHandler, CUnknown
  607. {
  608.     private:
  609.         CLSID _clsid;
  610.         IADsPathname *_padp;
  611.         IDsDisplaySpecifier *_pdds;
  612.         DWORD _dwFlags;
  613.         LPWSTR _pszServer;
  614.         LPWSTR _pszUserName;
  615.         LPWSTR _pszPassword;
  616.     public:
  617.         CQueryThreadCH(REFCLSID rCLSID);
  618.         ~CQueryThreadCH();
  619.         // *** IUnknown ***
  620.         STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObject);
  621.         STDMETHOD_(ULONG, AddRef)();
  622.         STDMETHOD_(ULONG, Release)();
  623.         // *** IDsQueryColumnHandler ***
  624.         STDMETHOD(Initialize)(THIS_ DWORD dwFlags, LPCWSTR pszServer, LPCWSTR pszUserName, LPCWSTR pszPassword);
  625.         STDMETHOD(GetText)(ADS_SEARCH_COLUMN* psc, LPWSTR pszBuffer, INT cchBuffer);
  626. };
  627. //
  628. // constructor
  629. //
  630. CQueryThreadCH::CQueryThreadCH(REFCLSID rCLSID) :
  631.     _padp(NULL),
  632.     _pdds(NULL),
  633.     _clsid(rCLSID),
  634.     _dwFlags(0),
  635.     _pszServer(NULL),
  636.     _pszUserName(NULL),
  637.     _pszPassword(NULL)
  638. {
  639.     TraceEnter(TRACE_QUERYTHREAD, "CQueryThreadCH::CQueryThreadCH");
  640.     TraceGUID("CLSID of property: ", rCLSID);
  641.     TraceLeave();
  642. }
  643. CQueryThreadCH::~CQueryThreadCH()
  644. {
  645.     TraceEnter(TRACE_QUERYTHREAD, "CQueryThreadCH::~CQueryThreadCH");
  646.     DoRelease(_padp);       // free the name cracker
  647.     DoRelease(_pdds);
  648.     LocalFreeStringW(&_pszServer);
  649.     LocalFreeStringW(&_pszUserName);
  650.     LocalFreeStringW(&_pszPassword);
  651.     TraceLeave();
  652. }
  653. //
  654. // Handler IUnknown interface
  655. //
  656. #undef  CLASS_NAME
  657. #define CLASS_NAME CQueryThreadCH
  658. #include "unknown.inc"
  659. STDMETHODIMP CQueryThreadCH::QueryInterface(REFIID riid, LPVOID* ppvObject)
  660. {
  661.     INTERFACES iface[] =
  662.     {
  663.         &IID_IDsQueryColumnHandler, (IDsQueryColumnHandler*)this,
  664.     };
  665.     return HandleQueryInterface(riid, ppvObject, iface, ARRAYSIZE(iface));
  666. }
  667. //
  668. // Handle creating an instance of IDsFolderProperties for talking to WAB
  669. //
  670. STDAPI CQueryThreadCH_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  671. {
  672.     CQueryThreadCH *pqtch = new CQueryThreadCH(*poi->pclsid);
  673.     if ( !pqtch )
  674.         return E_OUTOFMEMORY;
  675.     HRESULT hres = pqtch->QueryInterface(IID_IUnknown, (void **)ppunk);
  676.     pqtch->Release();
  677.     return hres;
  678. }
  679. /*-----------------------------------------------------------------------------
  680. / IDsQueryColumnHandler
  681. /----------------------------------------------------------------------------*/
  682. STDMETHODIMP CQueryThreadCH::Initialize(THIS_ DWORD dwFlags, LPCWSTR pszServer, LPCWSTR pszUserName, LPCWSTR pszPassword)
  683. {
  684.     TraceEnter(TRACE_QUERYTHREAD, "CQueryThread::Initialize");
  685.     LocalFreeStringW(&_pszServer);
  686.     LocalFreeStringW(&_pszUserName);
  687.     LocalFreeStringW(&_pszPassword);
  688.     // copy new parameters away
  689.     _dwFlags = dwFlags;
  690.     HRESULT hres = LocalAllocStringW(&_pszServer, pszServer);    
  691.     if ( SUCCEEDED(hres) )
  692.         hres = LocalAllocStringW(&_pszUserName, pszUserName);
  693.         if ( SUCCEEDED(hres) )
  694.         hres = LocalAllocStringW(&_pszPassword, pszPassword);
  695.     DoRelease(_pdds)                                // discard previous IDisplaySpecifier object
  696.     TraceLeaveResult(hres);
  697. }
  698. STDMETHODIMP CQueryThreadCH::GetText(ADS_SEARCH_COLUMN* psc, LPWSTR pszBuffer, INT cchBuffer)
  699. {
  700.     HRESULT hres;
  701.     LPWSTR pValue = NULL;
  702.     USES_CONVERSION;
  703.     TraceEnter(TRACE_QUERYTHREAD, "CQueryThreadCH::GetText");
  704.     if ( !psc || !pszBuffer )
  705.         ExitGracefully(hres, E_UNEXPECTED, "Bad parameters passed to handler");
  706.     pszBuffer[0] = L''; 
  707.     if ( IsEqualCLSID(_clsid, CLSID_PublishedAtCH) || IsEqualCLSID(_clsid, CLSID_MachineOwnerCH) )
  708.     {
  709.         BOOL fPrefix = IsEqualCLSID(_clsid, CLSID_PublishedAtCH);
  710.         LPCWSTR pszPath = psc->pADsValues[0].DNString;
  711.         TraceAssert(pszPath != NULL);
  712.         // convert the ADsPath into its canonical form which is easier for the user
  713.         // to understand, CoCreate IADsPathname now instead of each time we call
  714.         // PrettyifyADsPathname.
  715.         if ( !_padp )
  716.         {
  717.             hres = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_IADsPathname, (LPVOID*)&_padp);
  718.             FailGracefully(hres, "Failed to get IADsPathname interface");
  719.         }
  720.         if ( FAILED(GetDisplayNameFromADsPath(pszPath, pszBuffer, cchBuffer, _padp, fPrefix)) )
  721.         {
  722.             TraceMsg("Failed to get display name from path");
  723.             StrCpyNW(pszBuffer, pszPath, cchBuffer);
  724.         }
  725.                                                                     
  726.         hres = S_OK;
  727.     }
  728.     else if ( IsEqualCLSID(_clsid, CLSID_ObjectClassCH) )
  729.     {        
  730.         // get a string from the search column, and then look up the friendly name of the
  731.         // class from its display specifier
  732.         hres = ObjectClassFromSearchColumn(psc, &pValue);
  733.         FailGracefully(hres, "Failed to get object class from psc");
  734.         if ( !_pdds )
  735.         {
  736.             DWORD dwFlags = 0;
  737.             hres = CoCreateInstance(CLSID_DsDisplaySpecifier, NULL, CLSCTX_INPROC_SERVER, IID_IDsDisplaySpecifier, (void **)&_pdds);
  738.             FailGracefully(hres, "Failed to get IDsDisplaySpecifier interface");
  739.             hres = _pdds->SetServer(_pszServer, _pszUserName, _pszPassword, DSSSF_DSAVAILABLE);
  740.             FailGracefully(hres, "Failed when setting server for display specifier object");                
  741.         }
  742.         _pdds->GetFriendlyClassName(pValue, pszBuffer, cchBuffer);
  743.     }
  744.     else if ( IsEqualCLSID(_clsid, CLSID_MachineOwnerCH) )
  745.     {
  746.         // convert the DN of the user object into a string that we can display
  747.     }
  748.     else if ( IsEqualCLSID(_clsid, CLSID_MachineRoleCH) )
  749.     {
  750.         // convert the userAccountControl value into something we can display for the user
  751.         if ( psc->dwADsType == ADSTYPE_INTEGER )
  752.         {
  753.             INT iType = psc->pADsValues->Integer;           // pick out the type
  754.             if ( (iType >= 4096) && (iType <= 8191) )
  755.             {
  756.                 TraceMsg("Returning WKS/SRV string");
  757.                 LoadStringW(GLOBAL_HINSTANCE, IDS_WKSORSERVER, pszBuffer, cchBuffer);
  758.             }
  759.             else if ( iType >= 8192 )
  760.             {
  761.                 TraceMsg("Returning DC string");
  762.                 LoadStringW(GLOBAL_HINSTANCE, IDS_DC, pszBuffer, cchBuffer);
  763.             }
  764.             else
  765.             {
  766.                 Trace(TEXT("Unknown type %x"), iType);
  767.             }
  768.         }
  769.     }
  770.     else
  771.     {
  772.         ExitGracefully(hres, E_UNEXPECTED, "m_clsid specifies column type not supported");
  773.     }
  774.     hres = S_OK;
  775. exit_gracefully:
  776.     LocalFreeStringW(&pValue);
  777.     TraceLeaveResult(hres);
  778. }