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.

846 lines
18 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. vststmsg.cxx
  5. Abstract:
  6. Implementation of test message classes for the server
  7. Brian Berkowitz [brianb] 05/22/2000
  8. TBD:
  9. Revision History:
  10. Name Date Comments
  11. brianb 05/22/2000 Created
  12. ssteiner 06/07/2000 Split client and server portions into
  13. two files. vststmsgclient.cxx contains
  14. the client portion.
  15. --*/
  16. #include <stdafx.h>
  17. #include <vststmsg.hxx>
  18. #include <vststmsghandler.hxx>
  19. void LogUnexpectedFailure(LPCWSTR wsz, ...);
  20. /*++
  21. Routine Description:
  22. This needs to be run once on the server side to install all of the message handlers.
  23. Arguments:
  24. None
  25. Return Value:
  26. <Enter return values here>
  27. --*/
  28. static void
  29. InstallServerMsgHandlers()
  30. {
  31. g_msgTypes[VSTST_MT_TEXT].pfnHandler = CVsTstMsgHandlerRoutines::PrintMessage;
  32. g_msgTypes[VSTST_MT_IMMEDIATETEXT].pfnHandler = CVsTstMsgHandlerRoutines::PrintMessage;
  33. g_msgTypes[VSTST_MT_FAILURE].pfnHandler = CVsTstMsgHandlerRoutines::HandleFailure;
  34. g_msgTypes[VSTST_MT_OPERATIONFAILURE].pfnHandler = CVsTstMsgHandlerRoutines::HandleOperationFailure;
  35. g_msgTypes[VSTST_MT_UNEXPECTEDEXCEPTION].pfnHandler = CVsTstMsgHandlerRoutines::HandleUnexpectedException;
  36. g_msgTypes[VSTST_MT_SUCCESS].pfnHandler = CVsTstMsgHandlerRoutines::HandleSuccess;
  37. }
  38. CVsTstMsgHandler::CVsTstMsgHandler(
  39. IN LPCWSTR pwszLogFileName
  40. ) :
  41. m_bcsQueueInitialized(false),
  42. m_pmsgFirst(NULL),
  43. m_pmsgLast(NULL),
  44. m_cbMaxMsgLength(0),
  45. m_hThreadReader(NULL),
  46. m_hThreadWorker(NULL),
  47. m_hevtWorker(NULL),
  48. m_hevtReader(NULL),
  49. m_bReadEnabled(false),
  50. m_bTerminateWorker(false),
  51. m_bTerminateReader(false),
  52. m_pipeList(NULL),
  53. m_bcsPipeListInitialized(false),
  54. m_cNtLog( pwszLogFileName )
  55. {
  56. //
  57. // Initialize the general message types array and install the message
  58. // handlers.
  59. //
  60. InitMsgTypes();
  61. InstallServerMsgHandlers();
  62. }
  63. // free data allocated by the class
  64. void CVsTstMsgHandler::FreeData()
  65. {
  66. if (m_bcsQueueInitialized)
  67. {
  68. m_csQueue.Term();
  69. m_bcsQueueInitialized = false;
  70. }
  71. if (m_bcsPipeListInitialized)
  72. {
  73. m_csPipeList.Term();
  74. m_bcsPipeListInitialized = false;
  75. }
  76. while(m_pmsgFirst)
  77. {
  78. VSTST_MSG_HDR *pmsgNext = m_pmsgFirst->pmsgNext;
  79. delete m_pmsgFirst;
  80. m_pmsgFirst = pmsgNext;
  81. }
  82. if (m_hThreadWorker)
  83. {
  84. CloseHandle(m_hThreadWorker);
  85. m_hThreadWorker = NULL;
  86. }
  87. if (m_hevtWorker)
  88. {
  89. CloseHandle(m_hevtWorker);
  90. m_hevtWorker = NULL;
  91. }
  92. if (m_hevtReader)
  93. {
  94. CloseHandle(m_hevtReader);
  95. m_hevtReader = NULL;
  96. }
  97. }
  98. CVsTstMsgHandler::~CVsTstMsgHandler()
  99. {
  100. ForceTermination();
  101. FreeData();
  102. }
  103. // initailize message handler and worker thread
  104. HRESULT CVsTstMsgHandler::Initialize(UINT cbMaxMsg)
  105. {
  106. m_cbMaxMsgLength = cbMaxMsg;
  107. try
  108. {
  109. m_csQueue.Init();
  110. m_bcsQueueInitialized = true;
  111. m_csPipeList.Init();
  112. m_bcsPipeListInitialized = true;
  113. }
  114. catch(...)
  115. {
  116. return E_UNEXPECTED;
  117. }
  118. HRESULT hr = S_OK;
  119. m_hevtWorker = CreateEvent(NULL, TRUE, FALSE, NULL);
  120. if (m_hevtWorker == NULL)
  121. goto _ErrExit;
  122. m_hevtReader = CreateEvent(NULL, TRUE, FALSE, NULL);
  123. if (m_hevtReader == NULL)
  124. goto _ErrExit;
  125. DWORD tid;
  126. m_hThreadWorker = CreateThread
  127. (
  128. NULL,
  129. 64*1024,
  130. StartWorkerThread,
  131. this,
  132. 0,
  133. &tid
  134. );
  135. if (m_hThreadWorker == NULL)
  136. goto _ErrExit;
  137. return S_OK;
  138. _ErrExit:
  139. hr = HRESULT_FROM_WIN32(GetLastError());
  140. FreeData();
  141. return hr;
  142. }
  143. // adjust message pointers
  144. bool CVsTstMsgHandler::AdjustPointers(VSTST_MSG_HDR *phdr)
  145. {
  146. VSTST_MSG_TYPE_TABLE *pType = &g_msgTypes[phdr->type];
  147. BYTE *pb = phdr->rgb;
  148. VOID **ppv = (VOID **) (pb + pType->cbFixed);
  149. pb = (BYTE *) (ppv + pType->cVarPtr);
  150. for(unsigned iVarPtr = 0; iVarPtr < pType->cVarPtr; iVarPtr++, ppv++)
  151. {
  152. *ppv = pb;
  153. size_t cb;
  154. switch(pType->pointerTypes[iVarPtr])
  155. {
  156. default:
  157. return false;
  158. case VSTST_VPT_BYTE:
  159. cb = *(UINT *) pb;
  160. break;
  161. case VSTST_VPT_ANSI:
  162. cb = strlen((char *) pb) + 1;
  163. break;
  164. case VSTST_VPT_UNICODE:
  165. cb = (wcslen((WCHAR *) pb) + 1) * sizeof(WCHAR);
  166. break;
  167. }
  168. // align to pointer boundary
  169. cb = (cb + sizeof(PVOID) - 1) & ~(sizeof(PVOID) - 1);
  170. pb += cb;
  171. }
  172. return true;
  173. }
  174. // process message immediately
  175. bool CVsTstMsgHandler::ProcessMsgImmediate(VSTST_MSG_HDR *phdr)
  176. {
  177. if (!AdjustPointers(phdr))
  178. return false;
  179. VSTST_MSG_TYPE_TABLE *pType = &g_msgTypes[phdr->type];
  180. try
  181. {
  182. pType->pfnHandler(phdr, &m_cNtLog);
  183. }
  184. catch(...)
  185. {
  186. return false;
  187. }
  188. return true;
  189. }
  190. // queue message for worker thrad
  191. bool CVsTstMsgHandler::QueueMsg(VSTST_MSG_HDR *phdr)
  192. {
  193. BYTE *pbMsg = new BYTE[phdr->cbMsg];
  194. if (pbMsg == NULL)
  195. // can't allocate message, wait for queue to of messages
  196. // to complete and then process messages serially
  197. {
  198. WaitForQueueToComplete();
  199. return ProcessMsgImmediate(phdr);
  200. }
  201. memcpy(pbMsg, phdr, phdr->cbMsg);
  202. VSTST_MSG_HDR *phdrT = (VSTST_MSG_HDR *) pbMsg;
  203. if (!AdjustPointers(phdrT))
  204. return false;
  205. m_csQueue.Lock();
  206. if (m_pmsgLast == NULL)
  207. {
  208. VSTST_ASSERT(m_pmsgFirst == NULL);
  209. m_pmsgLast = m_pmsgFirst = phdrT;
  210. SetEvent(m_hevtWorker);
  211. }
  212. else
  213. {
  214. VSTST_ASSERT(m_pmsgLast->pmsgNext == NULL);
  215. m_pmsgLast->pmsgNext = phdrT;
  216. // replace last element on queue
  217. m_pmsgLast = phdrT;
  218. }
  219. m_csQueue.Unlock();
  220. return true;
  221. }
  222. // execute items on the work queue
  223. bool CVsTstMsgHandler::DoWork()
  224. {
  225. // Add this thread as a participant
  226. m_cNtLog.AddParticipant();
  227. // For now start the one and only variation
  228. m_cNtLog.StartVariation( L"VssTestController" );
  229. while(TRUE)
  230. {
  231. // wait for something to get on the work queue or for 1 second
  232. if (WaitForSingleObject(m_hevtWorker, 1000) == WAIT_FAILED)
  233. {
  234. // Log severe error
  235. m_cNtLog.Log( eSevLev_Severe, L"CVsTstMsgHandler::DoWork, WaitForSingleObject returned WAIT_FAILED, dwRet: %d", ::GetLastError() );
  236. // End the variation
  237. m_cNtLog.EndVariation();
  238. // Remove the thread as a participant
  239. m_cNtLog.RemoveParticipant();
  240. return false;
  241. }
  242. while(TRUE)
  243. {
  244. // lock queue
  245. m_csQueue.Lock();
  246. // check whether queue is empty
  247. if (m_pmsgFirst == NULL)
  248. {
  249. VSTST_ASSERT(m_pmsgLast == NULL);
  250. // check whether we should terminate the thread
  251. if (m_bTerminateWorker)
  252. {
  253. // terminate thread
  254. m_csQueue.Unlock();
  255. // End the variation
  256. m_cNtLog.EndVariation();
  257. // Remove the thread as a participant
  258. m_cNtLog.RemoveParticipant();
  259. return true;
  260. }
  261. // setup to wait again
  262. ResetEvent(m_hevtWorker);
  263. m_csQueue.Unlock();
  264. break;
  265. }
  266. // pull first message off of queue
  267. VSTST_MSG_HDR *phdr = m_pmsgFirst;
  268. // move head of queue to next element
  269. m_pmsgFirst = m_pmsgFirst->pmsgNext;
  270. // is queue now empty
  271. if (m_pmsgFirst == NULL)
  272. {
  273. VSTST_ASSERT(m_pmsgLast == phdr);
  274. // set tail of queue to null
  275. m_pmsgLast = NULL;
  276. }
  277. // unlock queue before executing item
  278. m_csQueue.Unlock();
  279. // execute item
  280. VSTST_MSG_TYPE_TABLE *pType = &g_msgTypes[phdr->type];
  281. try
  282. {
  283. pType->pfnHandler(phdr, &m_cNtLog );
  284. }
  285. catch(...)
  286. {
  287. // Log severe error
  288. m_cNtLog.Log( eSevLev_Severe, L"CVsTstMsgHandler::DoWork, caught unexpected exception from message handler" );
  289. // End the variation
  290. m_cNtLog.EndVariation();
  291. // Remove the thread as a participant
  292. m_cNtLog.RemoveParticipant();
  293. return false;
  294. }
  295. }
  296. }
  297. return true;
  298. }
  299. // terminate worker thread, waiting for all work to complete
  300. void CVsTstMsgHandler::WaitForQueueToComplete()
  301. {
  302. m_bTerminateWorker = true;
  303. if (WaitForSingleObject(m_hThreadWorker, INFINITE) == WAIT_FAILED)
  304. {
  305. // polling way to wait if we wait fails. Note that we usually
  306. // would only expect to get here in stress situations
  307. while(TRUE)
  308. {
  309. m_csQueue.Lock();
  310. if (m_pmsgFirst == NULL)
  311. {
  312. m_csQueue.Unlock();
  313. break;
  314. }
  315. m_csQueue.Unlock();
  316. Sleep(100);
  317. }
  318. }
  319. }
  320. DWORD CVsTstMsgHandler::StartWorkerThread(VOID *pv)
  321. {
  322. CVsTstMsgHandler *pHandler = (CVsTstMsgHandler *) pv;
  323. try
  324. {
  325. pHandler->DoWork();
  326. }
  327. catch(...)
  328. {
  329. LogUnexpectedFailure(L"Worker thread unexpectedly terminated");
  330. }
  331. return 0;
  332. }
  333. void CVsTstMsgHandler::StartProcessingMessages()
  334. {
  335. m_bReadEnabled = true;
  336. SetEvent(m_hevtReader);
  337. }
  338. void CVsTstMsgHandler::StopProcessingMessages()
  339. {
  340. ResetEvent(m_hevtReader);
  341. m_bReadEnabled = false;
  342. }
  343. void CVsTstMsgHandler::ForceTermination()
  344. {
  345. m_bReadEnabled = false;
  346. m_bTerminateWorker = true;
  347. m_bTerminateReader = true;
  348. SetEvent(m_hevtReader);
  349. if (m_hThreadWorker)
  350. {
  351. DWORD dwErr = WaitForSingleObject(m_hThreadWorker, 5000);
  352. if (dwErr == WAIT_FAILED || dwErr == WAIT_TIMEOUT)
  353. // force termination of worker thread
  354. TerminateThread(m_hThreadWorker, 1);
  355. CloseHandle(m_hThreadWorker);
  356. m_hThreadWorker = NULL;
  357. }
  358. m_csPipeList.Lock();
  359. while(m_pipeList != NULL)
  360. {
  361. CVsTstPipe *pipe = m_pipeList;
  362. HANDLE hThread = pipe->m_hThreadReader;
  363. pipe->m_hThreadReader = NULL;
  364. m_csPipeList.Unlock();
  365. m_pipeList->ForceTermination(hThread);
  366. m_csPipeList.Lock();
  367. if (m_pipeList == pipe)
  368. delete m_pipeList;
  369. }
  370. m_csPipeList.Unlock();
  371. }
  372. // launch a pipe reader thread
  373. HRESULT CVsTstMsgHandler::LaunchReader()
  374. {
  375. CVsTstPipe *pipe = new CVsTstPipe(this);
  376. if (pipe == NULL)
  377. return E_OUTOFMEMORY;
  378. HRESULT hr = pipe->Initialize(m_cbMaxMsgLength);
  379. if (FAILED(hr))
  380. {
  381. delete pipe;
  382. return hr;
  383. }
  384. return S_OK;
  385. }
  386. // link pipe into pipe list
  387. void CVsTstMsgHandler::LinkPipe(CVsTstPipe *pipe)
  388. {
  389. VSTST_ASSERT(!pipe->m_bLinked);
  390. m_csPipeList.Lock();
  391. pipe->m_prev = NULL;
  392. pipe->m_next = m_pipeList;
  393. if (m_pipeList)
  394. {
  395. VSTST_ASSERT(m_pipeList->m_prev == NULL);
  396. m_pipeList->m_prev = pipe;
  397. }
  398. m_pipeList = pipe;
  399. m_csPipeList.Unlock();
  400. pipe->m_bLinked = true;
  401. }
  402. // unlink pipe from pipe list
  403. void CVsTstMsgHandler::UnlinkPipe(CVsTstPipe *pipe)
  404. {
  405. VSTST_ASSERT(pipe->m_bLinked);
  406. m_csPipeList.Lock();
  407. if (pipe->m_prev == NULL)
  408. {
  409. VSTST_ASSERT(m_pipeList == pipe);
  410. m_pipeList = pipe->m_next;
  411. if (m_pipeList)
  412. {
  413. VSTST_ASSERT(m_pipeList->m_prev == pipe);
  414. m_pipeList->m_prev = NULL;
  415. }
  416. }
  417. else
  418. {
  419. VSTST_ASSERT(pipe->m_prev->m_next == pipe);
  420. pipe->m_prev->m_next = pipe->m_next;
  421. if (pipe->m_next)
  422. {
  423. VSTST_ASSERT(pipe->m_next->m_prev == pipe);
  424. pipe->m_next->m_prev = pipe->m_prev;
  425. }
  426. }
  427. m_csPipeList.Unlock();
  428. pipe->m_bLinked = false;
  429. }
  430. // constructor for a pipe
  431. CVsTstPipe::CVsTstPipe(CVsTstMsgHandler *pHandler) :
  432. m_hPipe(NULL),
  433. m_hevtOverlapped(NULL),
  434. m_rgbMsg(NULL),
  435. m_cbMsg(0),
  436. m_hThreadReader(NULL),
  437. m_bLinked(NULL),
  438. m_pHandler(pHandler),
  439. m_bConnected(false)
  440. {
  441. }
  442. // destructor for a pipe
  443. CVsTstPipe::~CVsTstPipe()
  444. {
  445. // unlink pipe from list if linked
  446. if (m_bLinked)
  447. {
  448. VSTST_ASSERT(m_pHandler);
  449. m_pHandler->UnlinkPipe(this);
  450. }
  451. FreeData();
  452. }
  453. // initailize message handler and worker thread
  454. HRESULT CVsTstPipe::Initialize(UINT cbMaxMsg)
  455. {
  456. HRESULT hr = S_OK;
  457. // create pipe
  458. m_hPipe = CreateNamedPipe
  459. (
  460. s_wszPipeName,
  461. FILE_FLAG_OVERLAPPED|PIPE_ACCESS_INBOUND,
  462. PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT,
  463. PIPE_UNLIMITED_INSTANCES,
  464. 0,
  465. cbMaxMsg,
  466. 100,
  467. NULL
  468. );
  469. if (m_hPipe == INVALID_HANDLE_VALUE)
  470. return HRESULT_FROM_WIN32(GetLastError());
  471. // create message buffer
  472. m_cbMsg = cbMaxMsg;
  473. m_rgbMsg = new BYTE[m_cbMsg];
  474. if (m_rgbMsg == NULL)
  475. {
  476. hr = E_OUTOFMEMORY;
  477. goto _ErrCleanup;
  478. }
  479. // create overlapped read event
  480. m_hevtOverlapped = CreateEvent(NULL, TRUE, FALSE, NULL);
  481. if (m_hevtOverlapped == NULL)
  482. goto _ErrExit;
  483. // create reader thread
  484. DWORD tid;
  485. m_hThreadReader = CreateThread
  486. (
  487. NULL,
  488. 256*1024,
  489. StartReaderThread,
  490. this,
  491. 0,
  492. &tid
  493. );
  494. if (m_hThreadReader == NULL)
  495. goto _ErrExit;
  496. // link pipe into list
  497. m_pHandler->LinkPipe(this);
  498. return S_OK;
  499. _ErrExit:
  500. hr = HRESULT_FROM_WIN32(GetLastError());
  501. _ErrCleanup:
  502. FreeData();
  503. return hr;
  504. }
  505. // free data allocated by the class
  506. void CVsTstPipe::FreeData()
  507. {
  508. delete m_rgbMsg;
  509. m_rgbMsg = NULL;
  510. if (m_hPipe != INVALID_HANDLE_VALUE)
  511. {
  512. // disconnect pipe if connected
  513. if (m_bConnected)
  514. {
  515. DisconnectNamedPipe(m_hPipe);
  516. m_bConnected = false;
  517. }
  518. CloseHandle(m_hPipe);
  519. m_hPipe = NULL;
  520. }
  521. if (m_hThreadReader)
  522. {
  523. CloseHandle(m_hThreadReader);
  524. m_hThreadReader = NULL;
  525. }
  526. if (m_hevtOverlapped)
  527. {
  528. CloseHandle(m_hevtOverlapped);
  529. m_hevtOverlapped = NULL;
  530. }
  531. }
  532. void CVsTstPipe::ForceTermination(HANDLE hThread)
  533. {
  534. // thread should already be terminated
  535. DWORD dwErr = WaitForSingleObject(hThread, 5000);
  536. if (dwErr == WAIT_FAILED || dwErr == WAIT_TIMEOUT)
  537. // force termination of thread
  538. TerminateThread(hThread, 1);
  539. CloseHandle(hThread);
  540. }
  541. // setup overlapped I/O structure used for reading data
  542. // from the pipe
  543. void CVsTstPipe::SetupOverlapped()
  544. {
  545. VSTST_ASSERT(m_hevtOverlapped);
  546. ResetEvent(m_hevtOverlapped);
  547. memset(&m_overlap, 0, sizeof(m_overlap));
  548. m_overlap.hEvent = m_hevtOverlapped;
  549. }
  550. bool CVsTstPipe::WaitForConnection()
  551. {
  552. SetupOverlapped();
  553. if (ConnectNamedPipe(m_hPipe, &m_overlap))
  554. return true;
  555. if (GetLastError() == ERROR_IO_PENDING)
  556. {
  557. while(TRUE)
  558. {
  559. if (!m_pHandler->m_bReadEnabled)
  560. {
  561. CancelIo(m_hPipe);
  562. return false;
  563. }
  564. DWORD dwErr = WaitForSingleObject(m_hevtOverlapped, 1000);
  565. if (dwErr == WAIT_OBJECT_0)
  566. break;
  567. if (dwErr == WAIT_FAILED)
  568. {
  569. Sleep(1000);
  570. CancelIo(m_hPipe);
  571. return false;
  572. }
  573. }
  574. }
  575. return true;
  576. }
  577. // do the work of reading messages
  578. VSTST_READER_STATUS CVsTstPipe::ReadMessages(bool bConnect)
  579. {
  580. if (bConnect)
  581. {
  582. if (!WaitForConnection())
  583. return VSTST_RS_NOTCONNECTED;
  584. // launch a new reader thread to wait for the next connection
  585. m_pHandler->LaunchReader();
  586. }
  587. // while we are doing reads
  588. while(m_pHandler->m_bReadEnabled)
  589. {
  590. DWORD cbRead;
  591. // setup overlapped structure
  592. SetupOverlapped();
  593. if (!ReadFile
  594. (
  595. m_hPipe,
  596. m_rgbMsg,
  597. m_cbMsg,
  598. &cbRead,
  599. &m_overlap
  600. ))
  601. {
  602. DWORD dwErr = GetLastError();
  603. if (dwErr == ERROR_IO_PENDING)
  604. {
  605. while(TRUE)
  606. {
  607. if (!m_pHandler->m_bReadEnabled)
  608. {
  609. CancelIo(m_hPipe);
  610. return VSTST_RS_READDISABLED;
  611. }
  612. DWORD dwErr = WaitForSingleObject(m_hevtOverlapped, 1000);
  613. if (dwErr == WAIT_OBJECT_0)
  614. break;
  615. if (dwErr == WAIT_FAILED)
  616. {
  617. Sleep(1000);
  618. CancelIo(m_hPipe);
  619. continue;
  620. }
  621. }
  622. if (!GetOverlappedResult(m_hPipe, &m_overlap, &cbRead, FALSE))
  623. {
  624. CancelIo(m_hPipe);
  625. continue;
  626. }
  627. }
  628. else
  629. {
  630. // unexpected error reading from pipe
  631. DWORD dwErr = GetLastError();
  632. if (dwErr == ERROR_BROKEN_PIPE)
  633. {
  634. // terminate thread as we are no longer reading from
  635. // the pipe
  636. DisconnectNamedPipe(m_hPipe);
  637. return VSTST_RS_DISCONNECTED;
  638. }
  639. else
  640. ReadPipeError(dwErr);
  641. return VSTST_RS_ERROR;
  642. }
  643. }
  644. VSTST_MSG_HDR *phdr = (VSTST_MSG_HDR *) m_rgbMsg;
  645. if (phdr->cbMsg != cbRead ||
  646. phdr->type == VSTST_MT_UNDEFINED ||
  647. phdr->type >= VSTST_MT_MAXMSGTYPE)
  648. LogInvalidMessage(phdr);
  649. else if (g_msgTypes[phdr->type].priority == VSTST_MP_IMMEDIATE)
  650. {
  651. if (!m_pHandler->ProcessMsgImmediate(phdr))
  652. LogInvalidMessage(phdr);
  653. }
  654. else
  655. {
  656. if (!m_pHandler->QueueMsg(phdr))
  657. LogInvalidMessage(phdr);
  658. }
  659. }
  660. return VSTST_RS_READDISABLED;
  661. }
  662. DWORD CVsTstPipe::StartReaderThread(VOID *pv)
  663. {
  664. CVsTstPipe *pipe = (CVsTstPipe *) pv;
  665. bool bConnected = false;
  666. try
  667. {
  668. while(!pipe->m_pHandler->m_bTerminateReader)
  669. {
  670. if (WaitForSingleObject(pipe->m_pHandler->m_hevtReader, INFINITE) == WAIT_FAILED)
  671. break;
  672. VSTST_READER_STATUS status = pipe->ReadMessages(!bConnected);
  673. if (status == VSTST_RS_DISCONNECTED)
  674. {
  675. bConnected = false;
  676. break;
  677. }
  678. else if (status != VSTST_RS_NOTCONNECTED)
  679. bConnected = true;
  680. }
  681. }
  682. catch(...)
  683. {
  684. LogUnexpectedFailure(L"Read thread unexpectedly terminated");
  685. }
  686. delete pipe;
  687. return 0;
  688. }