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.

509 lines
13 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows/NT **/
  3. /** Copyright(c) Microsoft Corporation, 1997 - 1999 **/
  4. /**********************************************************************/
  5. /*
  6. queryobj.cpp
  7. Implementation for nodes in the MMC
  8. FILE HISTORY:
  9. */
  10. #include "stdafx.h"
  11. #include "queryobj.h"
  12. #ifdef _DEBUG
  13. #define new DEBUG_NEW
  14. #undef THIS_FILE
  15. static char THIS_FILE[] = __FILE__;
  16. #endif
  17. /////////////////////////////////////////////////////////////////////
  18. //
  19. // CBackgroundThread
  20. //
  21. /////////////////////////////////////////////////////////////////////
  22. DEBUG_DECLARE_INSTANCE_COUNTER(CBackgroundThread);
  23. CBackgroundThread::CBackgroundThread()
  24. {
  25. DEBUG_INCREMENT_INSTANCE_COUNTER(CBackgroundThread);
  26. m_bAutoDelete = TRUE;
  27. ::InitializeCriticalSection(&m_cs);
  28. }
  29. CBackgroundThread::~CBackgroundThread()
  30. {
  31. DEBUG_DECREMENT_INSTANCE_COUNTER(CBackgroundThread);
  32. // Trace0("CBackgroundThread::~CBackgroundThread()\n");
  33. ::DeleteCriticalSection(&m_cs);
  34. m_spQuery.Release();
  35. }
  36. void
  37. CBackgroundThread::SetQueryObj(ITFSQueryObject *pQuery)
  38. {
  39. Assert(pQuery != NULL);
  40. m_spQuery.Set(pQuery);
  41. }
  42. BOOL CBackgroundThread::Start()
  43. {
  44. // NOTE::: ericdav 10/23/97
  45. // the thread is initially suspended so we can duplicate the handle
  46. // if the query object exits very quickly, the background thread object
  47. // may be destroyed before we can duplicate the handle. Right after
  48. // we duplicate the handle, it is started.
  49. return CreateThread(CREATE_SUSPENDED);
  50. }
  51. int
  52. CBackgroundThread::Run()
  53. {
  54. DWORD dwRet;
  55. DWORD dwData;
  56. BOOL fAbort = FALSE;
  57. Assert(m_spQuery);
  58. // Trace0("CBackgroundThread::Run() started\n");
  59. for (;;)
  60. {
  61. try
  62. {
  63. if (m_spQuery->Execute() != hrOK)
  64. break;
  65. }
  66. catch(...)
  67. {
  68. // Trace1("%x Caught an exception while executing CQuerObj!\n",
  69. // GetCurrentThreadId());
  70. fAbort = TRUE;
  71. }
  72. //$ Review: kennt
  73. // Should we sleep a little while at this point? especially
  74. // since the thread has given us some data to process.
  75. // Check to see if the abort flag is set
  76. if (fAbort || FHrOK(m_spQuery->FCheckForAbort()))
  77. {
  78. break;
  79. }
  80. }
  81. // Notify the query object that we are exiting
  82. if (fAbort || FHrOK(m_spQuery->FCheckForAbort()))
  83. m_spQuery->OnEventAbort();
  84. else
  85. m_spQuery->OnThreadExit();
  86. m_spQuery->DoCleanup();
  87. Trace2("handle=%X id=%X CBackgroundThread::Run() terminated\n",
  88. m_hThread, m_nThreadID);
  89. return 0;
  90. }
  91. /*---------------------------------------------------------------------------
  92. CQueryObject implementation
  93. ---------------------------------------------------------------------------*/
  94. DEBUG_DECLARE_INSTANCE_COUNTER(CQueryObject);
  95. /*!--------------------------------------------------------------------------
  96. CQueryObject::CQueryObject
  97. -
  98. Author: KennT
  99. ---------------------------------------------------------------------------*/
  100. CQueryObject::CQueryObject()
  101. {
  102. DEBUG_INCREMENT_INSTANCE_COUNTER(CQueryObject);
  103. m_cRef = 1;
  104. m_hEventAbort = NULL;
  105. ::InitializeCriticalSection(&m_cs);
  106. }
  107. /*!--------------------------------------------------------------------------
  108. CQueryObject::~CQueryObject
  109. -
  110. Author: KennT
  111. ---------------------------------------------------------------------------*/
  112. CQueryObject::~CQueryObject()
  113. {
  114. DEBUG_DECREMENT_INSTANCE_COUNTER(CQueryObject);
  115. Assert(m_cRef == 0);
  116. ::DeleteCriticalSection(&m_cs);
  117. ::CloseHandle(m_hEventAbort);
  118. m_hEventAbort = 0;
  119. // Trace1("%X CQueryObject::~CQueryObject()\n", GetCurrentThreadId());
  120. }
  121. IMPLEMENT_ADDREF_RELEASE(CQueryObject)
  122. STDMETHODIMP CQueryObject::QueryInterface(REFIID riid, LPVOID *ppv)
  123. {
  124. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  125. // Is the pointer bad?
  126. if (ppv == NULL)
  127. return E_INVALIDARG;
  128. // Place NULL in *ppv in case of failure
  129. *ppv = NULL;
  130. // This is the non-delegating IUnknown implementation
  131. if (riid == IID_IUnknown)
  132. *ppv = (LPVOID) this;
  133. else if (riid == IID_ITFSQueryObject)
  134. *ppv = (ITFSQueryObject *) this;
  135. // If we're going to return an interface, AddRef it first
  136. if (*ppv)
  137. {
  138. ((LPUNKNOWN) *ppv)->AddRef();
  139. return hrOK;
  140. }
  141. else
  142. return E_NOINTERFACE;
  143. }
  144. /*!--------------------------------------------------------------------------
  145. CQueryObject::Init
  146. -
  147. Author: KennT
  148. ---------------------------------------------------------------------------*/
  149. STDMETHODIMP CQueryObject::Init(ITFSThreadHandler *pHandler, HWND hwndHidden, UINT uMsgBase)
  150. {
  151. Assert(m_spHandler == NULL);
  152. m_spHandler.Set(pHandler);
  153. m_hHiddenWnd = hwndHidden;
  154. m_uMsgBase = uMsgBase;
  155. m_hEventAbort = ::CreateEvent(NULL,
  156. TRUE /*bManualReset*/,
  157. FALSE /*signalled*/,
  158. NULL);
  159. if (m_hEventAbort == NULL)
  160. return HRESULT_FROM_WIN32(GetLastError());
  161. else
  162. return hrOK;
  163. }
  164. /*!--------------------------------------------------------------------------
  165. CQueryObject::SetAbortEvent
  166. -
  167. Author: KennT
  168. ---------------------------------------------------------------------------*/
  169. STDMETHODIMP CQueryObject::SetAbortEvent()
  170. {
  171. // Trace1("%X Signalling CQueryObject abort event.\n", GetCurrentThreadId());
  172. Assert(m_hEventAbort);
  173. ::SetEvent(m_hEventAbort);
  174. OnEventAbort();
  175. // flush out the message queue in case something is wait to be processed
  176. MSG msg;
  177. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  178. {
  179. TranslateMessage(&msg);
  180. DispatchMessage(&msg);
  181. }
  182. return hrOK;
  183. }
  184. /*!--------------------------------------------------------------------------
  185. CQueryObject::FCheckForAbort
  186. -
  187. Author: KennT
  188. ---------------------------------------------------------------------------*/
  189. STDMETHODIMP CQueryObject::FCheckForAbort()
  190. {
  191. // Assert(m_hEventAbort);
  192. // we may not be running as a background thread, but somebody may have
  193. // created this object to do somework... In which case this isn't valid,
  194. // and just return ok
  195. if (!m_hEventAbort)
  196. return hrOK;
  197. DWORD dwRet = WaitForSingleObjectEx(m_hEventAbort, 0, FALSE);
  198. #ifdef DEBUG
  199. // if (dwRet == WAIT_OBJECT_0)
  200. // Trace1("%X CQueryObject() detects an abort event!\n", GetCurrentThreadId());
  201. #endif
  202. return dwRet == WAIT_OBJECT_0 ? hrOK : hrFalse;
  203. }
  204. /*---------------------------------------------------------------------------
  205. CNodeQueryObject implementation
  206. ---------------------------------------------------------------------------*/
  207. CNodeQueryObject::~CNodeQueryObject()
  208. {
  209. // Trace2("%X CNodeQueryObject::~CNodeQueryObject has %d objects\n",
  210. // GetCurrentThreadId(), m_dataQueue.GetCount());
  211. Assert(m_dataQueue.IsEmpty());
  212. }
  213. /*!--------------------------------------------------------------------------
  214. CNodeQueryObject::AddToQueue
  215. -
  216. Author: KennT
  217. ---------------------------------------------------------------------------*/
  218. BOOL CNodeQueryObject::AddToQueue(ITFSNode *pNode)
  219. {
  220. BOOL bSleep = FALSE;
  221. Lock();
  222. //::Sleep(1000);
  223. LPQUEUEDATA pQData = new QUEUEDATA;
  224. pQData->Type = QDATA_PNODE;
  225. pQData->Data = reinterpret_cast<LPARAM>(pNode);
  226. BOOL bRes = NULL != m_dataQueue.AddTail(pQData);
  227. pNode->AddRef();
  228. if (IsQueueFull())
  229. {
  230. bSleep = TRUE;
  231. }
  232. Unlock();
  233. // We have too much data, we've posted a notification to the node
  234. // so we can go to sleep here.
  235. // Note the danger here! The code calling has to be aware that a
  236. // context switch will occur here (as well as not locking the data
  237. // structures).
  238. if (bSleep)
  239. {
  240. PostHaveData((LPARAM) (CNodeQueryObject *) this);
  241. ::Sleep(0);
  242. }
  243. return bRes;
  244. }
  245. /*!--------------------------------------------------------------------------
  246. CNodeQueryObject::AddToQueue
  247. -
  248. Author: KennT
  249. ---------------------------------------------------------------------------*/
  250. BOOL CNodeQueryObject::AddToQueue(LPARAM Data, LPARAM Type)
  251. {
  252. BOOL bSleep = FALSE;
  253. Lock();
  254. //::Sleep(1000);
  255. LPQUEUEDATA pQData = new QUEUEDATA;
  256. pQData->Data = Data;
  257. pQData->Type = Type;
  258. BOOL bRes = NULL != m_dataQueue.AddTail(pQData);
  259. if (IsQueueFull())
  260. {
  261. bSleep = TRUE;
  262. }
  263. Unlock();
  264. // We have too much data, we've posted a notification to the node
  265. // so we can go to sleep here.
  266. // Note the danger here! The code calling has to be aware that a
  267. // context switch will occur here (as well as not locking the data
  268. // structures).
  269. if (bSleep)
  270. {
  271. PostHaveData((LPARAM) (CNodeQueryObject *) this);
  272. ::Sleep(0);
  273. }
  274. return bRes;
  275. }
  276. /*!--------------------------------------------------------------------------
  277. CNodeQueryObject::RemoveFromQueue
  278. -
  279. Author: KennT
  280. ---------------------------------------------------------------------------*/
  281. LPQUEUEDATA
  282. CNodeQueryObject::RemoveFromQueue()
  283. {
  284. Lock();
  285. LPQUEUEDATA pQD = m_dataQueue.IsEmpty() ? NULL : m_dataQueue.RemoveHead();
  286. Unlock();
  287. return pQD;
  288. }
  289. /*!--------------------------------------------------------------------------
  290. CNodeQueryObject::IsQueueEmpty
  291. -
  292. Author: KennT
  293. ---------------------------------------------------------------------------*/
  294. BOOL
  295. CNodeQueryObject::IsQueueEmpty()
  296. {
  297. Lock();
  298. BOOL bRes = m_dataQueue.IsEmpty();
  299. Unlock();
  300. return bRes;
  301. }
  302. /*!--------------------------------------------------------------------------
  303. CNodeQueryObject::IsQueueFull
  304. -
  305. Author: KennT
  306. ---------------------------------------------------------------------------*/
  307. BOOL CNodeQueryObject::IsQueueFull()
  308. {
  309. Lock();
  310. BOOL bRes = m_dataQueue.GetCount() >= m_nQueueCountMax;
  311. Unlock();
  312. return bRes;
  313. }
  314. /*!--------------------------------------------------------------------------
  315. CNodeQueryObject::OnThreadExit
  316. -
  317. Author: KennT
  318. ---------------------------------------------------------------------------*/
  319. STDMETHODIMP CNodeQueryObject::OnThreadExit()
  320. {
  321. BOOL fSomethingInQueue = FALSE;
  322. Lock();
  323. fSomethingInQueue = (m_dataQueue.GetCount() > 0);
  324. Unlock();
  325. // If there's anything in the queue, post
  326. if (fSomethingInQueue)
  327. {
  328. PostHaveData((LPARAM) (CNodeQueryObject *) this);
  329. ::Sleep(0);
  330. }
  331. return hrOK;
  332. }
  333. /*!--------------------------------------------------------------------------
  334. CNodeQueryObject::OnEventAbort
  335. -
  336. Author: KennT
  337. ---------------------------------------------------------------------------*/
  338. STDMETHODIMP CNodeQueryObject::OnEventAbort()
  339. {
  340. // Trace2("%X CNodeQueryObject::OnEventAbort Q has %d nodes.\n", GetCurrentThreadId(), m_dataQueue.GetCount());
  341. Lock();
  342. while (!m_dataQueue.IsEmpty())
  343. {
  344. LPQUEUEDATA pQD = m_dataQueue.RemoveHead();
  345. if (pQD->Type == QDATA_PNODE)
  346. {
  347. SPITFSNode spNode;
  348. spNode = reinterpret_cast<ITFSNode *>(pQD->Data);
  349. }
  350. else
  351. {
  352. // give the query object a chance to clean up this data
  353. OnEventAbort(pQD->Data, pQD->Type);
  354. }
  355. delete pQD;
  356. }
  357. Unlock();
  358. return hrOK;
  359. }
  360. /*!--------------------------------------------------------------------------
  361. CNodeQueryObject::OnCleanup
  362. DO NOT override this function. It provides a last cleanup
  363. mechanism for the query object. If you need notification
  364. that a thread is exiting, then override the OnThreadExit call.
  365. Author: EricDav
  366. ---------------------------------------------------------------------------*/
  367. STDMETHODIMP CNodeQueryObject::DoCleanup()
  368. {
  369. PostMessageToComponentData(WM_HIDDENWND_INDEX_EXITING, (LPARAM) (CNodeQueryObject *) this);
  370. m_spQuery.Release();
  371. return hrOK;
  372. }
  373. /*!--------------------------------------------------------------------------
  374. CNodeQueryObject::PostMessageToComponentData
  375. Posts a message to the hidden window to get back on the main
  376. MMC thread.
  377. Author: KennT
  378. ---------------------------------------------------------------------------*/
  379. BOOL
  380. CNodeQueryObject::PostMessageToComponentData(UINT uIndex, LPARAM lParam)
  381. {
  382. // Assert(m_spHandler);
  383. // Assert(m_hHiddenWnd != NULL);
  384. // Assert(::IsWindow(m_hHiddenWnd));
  385. //$ Review: kennt, if the hidden window is bogus, should we still post
  386. // to it? This could happen if our ComponentData went away but we were
  387. // still in our loop, posting away (we haven't had a chance to get the
  388. // abort signal).
  389. // maybe something like
  390. if (!m_hHiddenWnd)
  391. return 0;
  392. if (!::IsWindow(m_hHiddenWnd))
  393. {
  394. // Trace2("%X The Hidden window is GONE, tried to send %08x.\n",
  395. // GetCurrentThreadId(), m_uMsgBase+uIndex);
  396. m_hHiddenWnd = NULL;
  397. return 0;
  398. }
  399. //Trace2("%X CBackgroundThread::PostMessageToComponentData(%08x)\n", GetCurrentThreadId(), m_uMsgBase+uIndex);
  400. if (!m_spHandler)
  401. {
  402. // Trace0("PostMessageToCompData - m_spHandler == NULL, NOT posting a message\n");
  403. return 0;
  404. }
  405. return ::PostMessage(m_hHiddenWnd, m_uMsgBase + uIndex,
  406. (WPARAM)(ITFSThreadHandler *)m_spHandler, lParam);
  407. }
  408. /*---------------------------------------------------------------------------
  409. CNodeTimerQueryObject implementation
  410. ---------------------------------------------------------------------------*/
  411. HRESULT
  412. CNodeTimerQueryObject::Execute()
  413. {
  414. while (WaitForSingleObjectEx(m_hEventAbort, GetTimerInterval(), FALSE) != WAIT_OBJECT_0)
  415. {
  416. // we timed out. Post a message to the ComponentData...
  417. AddToQueue(NULL, QDATA_TIMER);
  418. }
  419. // Trace0("CNodeTimerQueryObject::Execute - got abort event, exiting.\n");
  420. return hrFalse;
  421. }