Leaked source code of windows server 2003
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.

767 lines
24 KiB

  1. // queryreq.cpp - Query request handler
  2. #include "stdafx.h"
  3. #include <process.h>
  4. #include "queryreq.h"
  5. #include "namemap.h"
  6. #include "resource.h"
  7. #include "util.h"
  8. #include "lmaccess.h"
  9. #include <algorithm>
  10. // singleton query thread object
  11. CQueryThread g_QueryThread;
  12. ////////////////////////////////////////////////////////////////////////////////////////////
  13. // class CQueryRequest
  14. //
  15. #define MSG_QUERY_START (WM_USER + 1)
  16. #define MSG_QUERY_REPLY (WM_USER + 2)
  17. // static members
  18. HWND CQueryRequest::m_hWndCB = NULL;
  19. // Forward ref
  20. LRESULT CALLBACK QueryRequestWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  21. HANDLE CQueryRequest::m_hMutex = NULL;
  22. // query window class object
  23. CMsgWindowClass QueryWndClass(L"BOMQueryHandler", QueryRequestWndProc);
  24. CQueryRequest::CQueryRequest()
  25. {
  26. m_cRef = 0;
  27. m_eState = QRST_INACTIVE;
  28. m_cPrefs = 0;
  29. m_paSrchPrefs = NULL;
  30. m_hrStatus = S_OK;
  31. m_pvstrAttr = NULL;
  32. m_pQueryCallback = NULL;
  33. }
  34. CQueryRequest::~CQueryRequest()
  35. {
  36. if (m_paSrchPrefs != NULL)
  37. delete m_paSrchPrefs;
  38. }
  39. HRESULT CQueryRequest::SetQueryParameters(LPCWSTR pszScope, LPCWSTR pszFilter, string_vector* pvstrClasses, string_vector* pvstrAttr)
  40. {
  41. if( !pszScope || !pszScope[0] || !pszFilter || !pvstrClasses || pvstrClasses->empty() ) return E_INVALIDARG;
  42. if( m_eState != QRST_INACTIVE ) return E_FAIL;
  43. m_strScope = pszScope;
  44. m_strFilter = pszFilter;
  45. m_vstrClasses = *pvstrClasses;
  46. m_pvstrAttr = pvstrAttr;
  47. return S_OK;
  48. }
  49. HRESULT CQueryRequest::SetSearchPreferences(ADS_SEARCHPREF_INFO* paSrchPrefs, int cPrefs)
  50. {
  51. m_cPrefs = cPrefs;
  52. if( cPrefs == 0 ) return S_OK; // Special case
  53. if( !paSrchPrefs ) return E_POINTER;
  54. if( m_eState != QRST_INACTIVE ) return E_FAIL;
  55. m_paSrchPrefs = new ADS_SEARCHPREF_INFO[cPrefs];
  56. if (m_paSrchPrefs == NULL) return E_OUTOFMEMORY;
  57. memcpy(m_paSrchPrefs, paSrchPrefs, cPrefs * sizeof(ADS_SEARCHPREF_INFO));
  58. return S_OK;
  59. }
  60. HRESULT CQueryRequest::SetCallback(CQueryCallback* pCallback, LPARAM lUserParam)
  61. {
  62. if( m_eState != QRST_INACTIVE ) return E_FAIL;
  63. m_pQueryCallback = pCallback;
  64. m_lUserParam = lUserParam;
  65. return S_OK;
  66. }
  67. HRESULT CQueryRequest::Start()
  68. {
  69. if( m_strScope.empty() || !m_pQueryCallback ) return E_FAIL;
  70. if( m_eState != QRST_INACTIVE ) return E_FAIL;
  71. // Create callback window the first time (m_hwndCB is static)
  72. if (m_hWndCB == NULL)
  73. m_hWndCB = QueryWndClass.Window();
  74. if (m_hWndCB == NULL) return E_FAIL;
  75. // Create mutex the first time (m_hMutex is static)
  76. if (m_hMutex == NULL)
  77. m_hMutex = CreateMutex(NULL, FALSE, NULL);
  78. if (m_hMutex == NULL) return E_FAIL;
  79. // Post request to query thread
  80. Lock();
  81. BOOL bStat = g_QueryThread.PostRequest(this);
  82. if (bStat)
  83. {
  84. m_eState = QRST_QUEUED;
  85. m_cRef++;
  86. }
  87. Unlock();
  88. return bStat ? S_OK : E_FAIL;
  89. }
  90. HRESULT CQueryRequest::Stop(BOOL bNotify)
  91. {
  92. HRESULT hr = S_OK;
  93. Lock();
  94. if (m_eState == QRST_QUEUED || m_eState == QRST_ACTIVE)
  95. {
  96. // Change state to stopped and notify the user if requested.
  97. // Don't release the query request here because the query thread needs
  98. // to see the new state. When the thread sees the stopped state it will
  99. // send a message to this thread's window proc, which will release the request.
  100. m_eState = QRST_STOPPED;
  101. if (bNotify)
  102. {
  103. ASSERT(m_pQueryCallback != NULL);
  104. m_pQueryCallback->QueryCallback(QRYN_STOPPED, this, m_lUserParam);
  105. }
  106. }
  107. else
  108. {
  109. hr = S_FALSE;
  110. }
  111. Unlock();
  112. return hr;
  113. }
  114. void CQueryRequest::Release()
  115. {
  116. ASSERT(m_cRef > 0);
  117. if (--m_cRef == 0)
  118. delete this;
  119. }
  120. void CQueryRequest::Execute()
  121. {
  122. // Move query to active state (if still in queued state)
  123. Lock();
  124. ASSERT(m_eState == QRST_QUEUED || m_eState == QRST_STOPPED);
  125. if (m_eState == QRST_STOPPED)
  126. {
  127. PostMessage(m_hWndCB, MSG_QUERY_REPLY, (WPARAM)this, (LPARAM)QRYN_STOPPED);
  128. Unlock();
  129. return;
  130. }
  131. m_eState = QRST_ACTIVE;
  132. Unlock();
  133. // Intiate the query
  134. CComPtr<IDirectorySearch> spDirSrch;
  135. ADS_SEARCH_HANDLE hSearch;
  136. LPCWSTR* paszAttr = NULL;
  137. LPCWSTR* paszNameAttr = NULL;
  138. do
  139. {
  140. // Create a directory search object
  141. m_hrStatus = ADsOpenObject(m_strScope.c_str(), NULL, NULL, ADS_SECURE_AUTHENTICATION, IID_IDirectorySearch, (LPVOID*)&spDirSrch);
  142. BREAK_ON_FAILURE(m_hrStatus)
  143. if (m_cPrefs != 0)
  144. {
  145. m_hrStatus = spDirSrch->SetSearchPreference(m_paSrchPrefs, m_cPrefs);
  146. BREAK_ON_FAILURE(m_hrStatus)
  147. }
  148. // Get naming attribute for each query class
  149. // This will be the attribute placed in column [0] of each RowItem
  150. paszNameAttr = new LPCWSTR[m_vstrClasses.size()];
  151. if (paszNameAttr == NULL)
  152. {
  153. m_hrStatus = E_OUTOFMEMORY;
  154. break;
  155. }
  156. for (int i = 0; i < m_vstrClasses.size(); i++)
  157. {
  158. // get display name map for this class
  159. DisplayNameMap* pNameMap = DisplayNames::GetMap(m_vstrClasses[i].c_str());
  160. ASSERT(pNameMap != NULL);
  161. // Save pointer to naming attribute
  162. paszNameAttr[i] = pNameMap->GetNameAttribute();
  163. }
  164. // Create array of attribute name ptrs for ExecuteSearch
  165. // Include space for user selected attribs, naming attribs, class and object path (distinguised name)
  166. paszAttr = new LPCWSTR[m_pvstrAttr->size() + m_vstrClasses.size() + 3];
  167. if (paszAttr == NULL)
  168. {
  169. m_hrStatus = E_OUTOFMEMORY;
  170. break;
  171. }
  172. int cAttr = 0;
  173. // add user selected attributes
  174. // These must be first because the column loop in the query code below indexes through them
  175. for (i=0; i < m_pvstrAttr->size(); i++)
  176. paszAttr[cAttr++] = const_cast<LPWSTR>((*m_pvstrAttr)[i].c_str());
  177. // add class naming attributes
  178. for (i = 0; i < m_vstrClasses.size(); i++)
  179. {
  180. // Multiple classes can use the same name so check for dup before adding
  181. int j = 0;
  182. while (j < i && wcscmp(paszNameAttr[i], paszNameAttr[j]) != 0) j++;
  183. if (j == i)
  184. paszAttr[cAttr++] = paszNameAttr[i];
  185. }
  186. // add path attribute
  187. paszAttr[cAttr++] = L"distinguishedName";
  188. // add class attribute
  189. paszAttr[cAttr++] = L"objectClass";
  190. // add user state attribute
  191. paszAttr[cAttr++] = L"userAccountControl";
  192. // Add (& ... ) around the query because DSQuery leaves it off
  193. // and GetNextRow causes heap error or endless query without it
  194. Lock();
  195. m_strFilter.insert(0, L"(&"),
  196. m_strFilter.append(L")");
  197. // Initiate search
  198. m_hrStatus = spDirSrch->ExecuteSearch((LPWSTR)m_strFilter.c_str(), (LPWSTR*)paszAttr, cAttr, &hSearch);
  199. Unlock();
  200. BREAK_ON_FAILURE(m_hrStatus)
  201. } while (FALSE);
  202. // If search failed, change query state and send failure message
  203. if (FAILED(m_hrStatus))
  204. {
  205. // Don't do anything if query has already been stopped
  206. Lock();
  207. if (m_eState == QRST_ACTIVE)
  208. {
  209. m_eState = QRST_FAILED;
  210. PostMessage(m_hWndCB, MSG_QUERY_REPLY, (WPARAM)this, (LPARAM)QRYN_FAILED);
  211. }
  212. Unlock();
  213. delete [] paszAttr;
  214. paszAttr = NULL;
  215. delete [] paszNameAttr;
  216. paszNameAttr = NULL;
  217. return;
  218. }
  219. // Get class map for translating class names
  220. DisplayNameMap* pNameMap = DisplayNames::GetClassMap();
  221. if( !pNameMap ) return;
  222. // Get Results
  223. int nItems = 0;
  224. while (nItems < MAX_RESULT_ITEMS && spDirSrch->GetNextRow(hSearch) == S_OK)
  225. {
  226. ADS_SEARCH_COLUMN col;
  227. // Allocate row item for user attributes plus fixed attributes (name & class)
  228. CRowItem* pRowItem = new CRowItem(m_pvstrAttr->size() + ROWITEM_USER_INDEX);
  229. if (pRowItem == NULL)
  230. {
  231. m_hrStatus = E_OUTOFMEMORY;
  232. break;
  233. }
  234. // Get path attribute
  235. if (spDirSrch->GetColumn(hSearch, L"distinguishedName", &col) == S_OK)
  236. {
  237. pRowItem->SetObjPath(col.pADsValues->CaseIgnoreString);
  238. spDirSrch->FreeColumn(&col);
  239. }
  240. // Get class attribute
  241. if (spDirSrch->GetColumn(hSearch, L"objectClass", &col) == S_OK)
  242. {
  243. // Class name is last element of multivalued objectClass attribute
  244. ASSERT(col.dwADsType == ADSTYPE_CASE_IGNORE_STRING);
  245. LPWSTR pszClass = col.pADsValues[col.dwNumValues-1].CaseIgnoreString;
  246. // Put class display name in row item
  247. pRowItem->SetAttribute(ROWITEM_CLASS_INDEX, pNameMap->GetAttributeDisplayName(pszClass));
  248. // Find class name in query classes vector
  249. string_vector::iterator itClass = std::find(m_vstrClasses.begin(), m_vstrClasses.end(), pszClass);
  250. // if found, look up name attribute for this class and put it in the rowitem
  251. if (itClass != m_vstrClasses.end())
  252. {
  253. ADS_SEARCH_COLUMN colName;
  254. if (spDirSrch->GetColumn(hSearch, (LPWSTR)paszNameAttr[itClass - m_vstrClasses.begin()], &colName) == S_OK)
  255. {
  256. pRowItem->SetAttribute(ROWITEM_NAME_INDEX, colName.pADsValues->CaseIgnoreString);
  257. spDirSrch->FreeColumn(&colName);
  258. }
  259. }
  260. else
  261. {
  262. // Use CN from path for the name
  263. LPCWSTR pszPath = pRowItem->GetObjPath();
  264. if( pszPath == NULL )
  265. {
  266. m_hrStatus = E_OUTOFMEMORY;
  267. break;
  268. }
  269. LPCWSTR pszSep;
  270. if (_tcsnicmp(pszPath, L"CN=", 3) == 0 && (pszSep = _tcschr(pszPath + 3, L',')) != NULL)
  271. {
  272. // Limit name to MAX_PATH chars
  273. int cch = pszSep - (pszPath + 3);
  274. if (cch >= MAX_PATH)
  275. cch = MAX_PATH - 1;
  276. // Create null-terminated CN string
  277. WCHAR szTemp[MAX_PATH];
  278. memcpy(szTemp, pszPath + 3, cch * sizeof(WCHAR));
  279. szTemp[cch] = 0;
  280. pRowItem->SetAttribute(ROWITEM_NAME_INDEX , szTemp);
  281. }
  282. else
  283. {
  284. ASSERT(0);
  285. }
  286. }
  287. spDirSrch->FreeColumn(&col);
  288. }
  289. // Set disabled status based on the value returned by AD
  290. if (SUCCEEDED(spDirSrch->GetColumn(hSearch, L"userAccountControl", &col)))
  291. {
  292. pRowItem->SetDisabled((col.pADsValues->Integer & UF_ACCOUNTDISABLE) != 0);
  293. spDirSrch->FreeColumn(&col);
  294. }
  295. // loop through all user attributes
  296. for (int iAttr = 0; iAttr < m_pvstrAttr->size(); ++iAttr)
  297. {
  298. HRESULT hr = spDirSrch->GetColumn(hSearch, (LPWSTR)paszAttr[iAttr], &col);
  299. if (SUCCEEDED(hr) && col.dwNumValues > 0)
  300. {
  301. WCHAR szBuf[MAX_PATH] = {0};
  302. LPWSTR psz = NULL;
  303. switch (col.dwADsType)
  304. {
  305. case ADSTYPE_DN_STRING:
  306. case ADSTYPE_CASE_EXACT_STRING:
  307. case ADSTYPE_PRINTABLE_STRING:
  308. case ADSTYPE_NUMERIC_STRING:
  309. case ADSTYPE_TYPEDNAME:
  310. case ADSTYPE_FAXNUMBER:
  311. case ADSTYPE_PATH:
  312. case ADSTYPE_OBJECT_CLASS:
  313. case ADSTYPE_CASE_IGNORE_STRING:
  314. psz = col.pADsValues->CaseIgnoreString;
  315. break;
  316. case ADSTYPE_BOOLEAN:
  317. if (col.pADsValues->Boolean)
  318. {
  319. static WCHAR szYes[16] = L"";
  320. if (szYes[0] == 0)
  321. {
  322. int nLen = ::LoadString(_Module.GetResourceInstance(), IDS_YES, szYes, lengthof(szYes));
  323. ASSERT(nLen != 0);
  324. }
  325. psz = szYes;
  326. }
  327. else
  328. {
  329. static WCHAR szNo[16] = L"";
  330. if (szNo[0] == 0)
  331. {
  332. int nLen = ::LoadString(_Module.GetResourceInstance(), IDS_NO, szNo, lengthof(szNo));
  333. ASSERT(nLen != 0);
  334. }
  335. psz = szNo;
  336. }
  337. break;
  338. case ADSTYPE_INTEGER:
  339. _snwprintf( szBuf, MAX_PATH-1, L"%d",col.pADsValues->Integer );
  340. psz = szBuf;
  341. break;
  342. case ADSTYPE_OCTET_STRING:
  343. if ( (_wcsicmp(col.pszAttrName, L"objectGUID") == 0) )
  344. {
  345. //Cast to LPGUID
  346. GUID* pObjectGUID = (LPGUID)(col.pADsValues->OctetString.lpValue);
  347. //Convert GUID to string.
  348. ::StringFromGUID2(*pObjectGUID, szBuf, 39);
  349. psz = szBuf;
  350. }
  351. break;
  352. case ADSTYPE_UTC_TIME:
  353. {
  354. SYSTEMTIME systemtime = col.pADsValues->UTCTime;
  355. DATE date;
  356. VARIANT varDate;
  357. if (SystemTimeToVariantTime(&systemtime, &date) != 0)
  358. {
  359. //Pack in variant.vt.
  360. varDate.vt = VT_DATE;
  361. varDate.date = date;
  362. if( SUCCEEDED(VariantChangeType(&varDate,&varDate, VARIANT_NOVALUEPROP, VT_BSTR)) )
  363. {
  364. wcsncpy(szBuf, varDate.bstrVal, MAX_PATH-1);
  365. }
  366. VariantClear(&varDate);
  367. }
  368. }
  369. break;
  370. case ADSTYPE_LARGE_INTEGER:
  371. {
  372. LARGE_INTEGER liValue;
  373. FILETIME filetime;
  374. DATE date;
  375. SYSTEMTIME systemtime;
  376. VARIANT varDate;
  377. liValue = col.pADsValues->LargeInteger;
  378. filetime.dwLowDateTime = liValue.LowPart;
  379. filetime.dwHighDateTime = liValue.HighPart;
  380. if((filetime.dwHighDateTime!=0) || (filetime.dwLowDateTime!=0))
  381. {
  382. //Check for properties of type LargeInteger that represent time.
  383. //If TRUE, then convert to variant time.
  384. if ((0==wcscmp(L"accountExpires", col.pszAttrName)) ||
  385. (0==wcscmp(L"badPasswordTime", col.pszAttrName))||
  386. (0==wcscmp(L"lastLogon", col.pszAttrName)) ||
  387. (0==wcscmp(L"lastLogoff", col.pszAttrName)) ||
  388. (0==wcscmp(L"lockoutTime", col.pszAttrName)) ||
  389. (0==wcscmp(L"pwdLastSet", col.pszAttrName))
  390. )
  391. {
  392. //Handle special case for Never Expires where low part is -1
  393. if (filetime.dwLowDateTime==-1)
  394. {
  395. psz = L"Never Expires";
  396. }
  397. else
  398. {
  399. if ( (FileTimeToLocalFileTime(&filetime, &filetime) != 0) &&
  400. (FileTimeToSystemTime(&filetime, &systemtime) != 0) &&
  401. (SystemTimeToVariantTime(&systemtime, &date) != 0) )
  402. {
  403. //Pack in variant.vt.
  404. varDate.vt = VT_DATE;
  405. varDate.date = date;
  406. if( SUCCEEDED(VariantChangeType(&varDate, &varDate, VARIANT_NOVALUEPROP,VT_BSTR)) )
  407. {
  408. wcsncpy( szBuf, varDate.bstrVal, lengthof(szBuf) );
  409. psz = szBuf;
  410. }
  411. VariantClear(&varDate);
  412. }
  413. }
  414. }
  415. else
  416. {
  417. //Print the LargeInteger.
  418. _snwprintf(szBuf, MAX_PATH-1, L"%d,%d",filetime.dwHighDateTime, filetime.dwLowDateTime);
  419. }
  420. }
  421. }
  422. break;
  423. case ADSTYPE_NT_SECURITY_DESCRIPTOR:
  424. break;
  425. }
  426. if (psz != NULL)
  427. hr = pRowItem->SetAttribute(iAttr + ROWITEM_USER_INDEX, psz);
  428. spDirSrch->FreeColumn(&col);
  429. }
  430. } // for user attributes
  431. // Add row to new rows vector and notify client
  432. Lock();
  433. // if query is still active
  434. if (m_eState == QRST_ACTIVE)
  435. {
  436. m_vRowsNew.push_back(*pRowItem);
  437. delete pRowItem;
  438. // notify if first new row
  439. if (m_vRowsNew.size() == 1)
  440. PostMessage(m_hWndCB, MSG_QUERY_REPLY, (WPARAM)this, (LPARAM)QRYN_NEWROWITEMS);
  441. Unlock();
  442. }
  443. else
  444. {
  445. delete pRowItem;
  446. Unlock();
  447. break;
  448. }
  449. }
  450. Lock();
  451. // If query wasn't stopped, then change state to completed and notify main thread
  452. if (m_eState == QRST_ACTIVE)
  453. {
  454. m_eState = QRST_COMPLETE;
  455. PostMessage(m_hWndCB, MSG_QUERY_REPLY, (WPARAM)this, (LPARAM)QRYN_COMPLETED);
  456. }
  457. else if (m_eState == QRST_STOPPED)
  458. {
  459. // if query was stopped, then acknowledge with notify so main thread can release the query req
  460. PostMessage(m_hWndCB, MSG_QUERY_REPLY, (WPARAM)this, (LPARAM)QRYN_STOPPED);
  461. }
  462. Unlock();
  463. spDirSrch->CloseSearchHandle(hSearch);
  464. delete [] paszAttr;
  465. delete [] paszNameAttr;
  466. }
  467. LRESULT CALLBACK QueryRequestWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
  468. {
  469. if (nMsg == MSG_QUERY_REPLY)
  470. {
  471. CQueryRequest* pQueryReq = reinterpret_cast<CQueryRequest*>(wParam);
  472. if( !pQueryReq ) return 0;
  473. QUERY_NOTIFY qryn = static_cast<QUERY_NOTIFY>(lParam);
  474. // Don't do any callbacks for a stopped query. Also, don't forward a stop notification.
  475. // The client receives a QRYN_STOPPED directly from the CQueryRequest::Stop() method.
  476. if (pQueryReq->m_eState != QRST_STOPPED && qryn != QRYN_STOPPED)
  477. pQueryReq->m_pQueryCallback->QueryCallback(qryn, pQueryReq, pQueryReq->m_lUserParam);
  478. // any notify but new row items indicates query is completed, so it can be released
  479. if (qryn != QRYN_NEWROWITEMS)
  480. pQueryReq->Release();
  481. return 0;
  482. }
  483. return DefWindowProc(hWnd, nMsg, wParam, lParam);
  484. }
  485. ////////////////////////////////////////////////////////////////////////////////////////////
  486. // class QueryThread
  487. //
  488. LRESULT CALLBACK QueryHandlerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  489. //-----------------------------------------------------------------------------
  490. // CQueryThread::StartThread
  491. //
  492. // Start the thread
  493. //-----------------------------------------------------------------------------
  494. BOOL CQueryThread::Start()
  495. {
  496. // If thread exists, just return
  497. if (m_hThread != NULL)
  498. return TRUE;
  499. BOOL bRet = FALSE;
  500. do // False loop
  501. {
  502. // Create start event
  503. m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  504. if (m_hEvent == NULL)
  505. break;
  506. // Start the thread
  507. m_hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, this, 0, &m_uThreadID);
  508. if (m_hThread == NULL)
  509. break;
  510. // Wait for start event
  511. DWORD dwEvStat = WaitForSingleObject(m_hEvent, 10000);
  512. if (dwEvStat != WAIT_OBJECT_0)
  513. break;
  514. bRet = TRUE;
  515. }
  516. while (0);
  517. ASSERT(bRet);
  518. // Clean up on failure
  519. if (!bRet)
  520. {
  521. if (m_hEvent)
  522. {
  523. CloseHandle(m_hEvent);
  524. m_hEvent = NULL;
  525. }
  526. if (m_hThread)
  527. {
  528. CloseHandle(m_hThread);
  529. m_hThread = NULL;
  530. }
  531. }
  532. return bRet;
  533. }
  534. void CQueryThread::Kill()
  535. {
  536. if (m_hThread != NULL)
  537. {
  538. PostThreadMessage(m_uThreadID, WM_QUIT, 0, 0);
  539. MSG msg;
  540. while (TRUE)
  541. {
  542. // Wait either for the thread to be signaled or any input event.
  543. DWORD dwStat = MsgWaitForMultipleObjects(1, &m_hThread, FALSE, INFINITE, QS_ALLINPUT);
  544. if (WAIT_OBJECT_0 == dwStat)
  545. break; // The thread is signaled.
  546. // There is one or more window message available.
  547. // Dispatch them and wait.
  548. if (PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))
  549. {
  550. TranslateMessage(&msg);
  551. DispatchMessage(&msg);
  552. }
  553. }
  554. CloseHandle(m_hThread);
  555. CloseHandle(m_hEvent);
  556. m_hThread = NULL;
  557. m_hEvent = NULL;
  558. }
  559. }
  560. BOOL CQueryThread::PostRequest(CQueryRequest* pQueryReq)
  561. {
  562. // make sure thread is active
  563. BOOL bStat = Start();
  564. if (bStat)
  565. bStat = PostThreadMessage(m_uThreadID, MSG_QUERY_START, (WPARAM)pQueryReq, (LPARAM)0);
  566. return bStat;
  567. }
  568. unsigned _stdcall CQueryThread::ThreadProc(void* pVoid )
  569. {
  570. ASSERT(pVoid != NULL);
  571. // Do a PeekMessage to create the message queue
  572. MSG msg;
  573. PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
  574. // Then signal that thread is started
  575. CQueryThread* pThread = reinterpret_cast<CQueryThread*>(pVoid);
  576. if( !pThread ) return 0;
  577. ASSERT(pThread->m_hEvent != NULL);
  578. SetEvent(pThread->m_hEvent);
  579. HRESULT hr = CoInitialize(NULL);
  580. RETURN_ON_FAILURE(hr);
  581. // Mesage loop
  582. while (TRUE)
  583. {
  584. long lStat = GetMessage(&msg, NULL, 0, 0);
  585. // zero => WM_QUIT received, so exit thread function
  586. if (lStat == 0)
  587. break;
  588. if (lStat > 0)
  589. {
  590. // Only process thread message of the expected type
  591. if (msg.hwnd == NULL && msg.message == MSG_QUERY_START)
  592. {
  593. CQueryRequest* pQueryReq = reinterpret_cast<CQueryRequest*>(msg.wParam);
  594. if( !pQueryReq ) break;
  595. pQueryReq->Execute();
  596. }
  597. else
  598. {
  599. DispatchMessage(&msg);
  600. }
  601. }
  602. } // WHILE (TRUE)
  603. CoUninitialize();
  604. return 0;
  605. }