Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1016 lines
33 KiB

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