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
846 lines
18 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
vststmsg.cxx
|
|
|
|
Abstract:
|
|
|
|
Implementation of test message classes for the server
|
|
|
|
|
|
Brian Berkowitz [brianb] 05/22/2000
|
|
|
|
TBD:
|
|
|
|
|
|
Revision History:
|
|
|
|
Name Date Comments
|
|
brianb 05/22/2000 Created
|
|
ssteiner 06/07/2000 Split client and server portions into
|
|
two files. vststmsgclient.cxx contains
|
|
the client portion.
|
|
|
|
--*/
|
|
|
|
|
|
#include <stdafx.h>
|
|
#include <vststmsg.hxx>
|
|
#include <vststmsghandler.hxx>
|
|
|
|
void LogUnexpectedFailure(LPCWSTR wsz, ...);
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This needs to be run once on the server side to install all of the message handlers.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
<Enter return values here>
|
|
|
|
--*/
|
|
static void
|
|
InstallServerMsgHandlers()
|
|
{
|
|
g_msgTypes[VSTST_MT_TEXT].pfnHandler = CVsTstMsgHandlerRoutines::PrintMessage;
|
|
g_msgTypes[VSTST_MT_IMMEDIATETEXT].pfnHandler = CVsTstMsgHandlerRoutines::PrintMessage;
|
|
g_msgTypes[VSTST_MT_FAILURE].pfnHandler = CVsTstMsgHandlerRoutines::HandleFailure;
|
|
g_msgTypes[VSTST_MT_OPERATIONFAILURE].pfnHandler = CVsTstMsgHandlerRoutines::HandleOperationFailure;
|
|
g_msgTypes[VSTST_MT_UNEXPECTEDEXCEPTION].pfnHandler = CVsTstMsgHandlerRoutines::HandleUnexpectedException;
|
|
g_msgTypes[VSTST_MT_SUCCESS].pfnHandler = CVsTstMsgHandlerRoutines::HandleSuccess;
|
|
}
|
|
|
|
CVsTstMsgHandler::CVsTstMsgHandler(
|
|
IN LPCWSTR pwszLogFileName
|
|
) :
|
|
m_bcsQueueInitialized(false),
|
|
m_pmsgFirst(NULL),
|
|
m_pmsgLast(NULL),
|
|
m_cbMaxMsgLength(0),
|
|
m_hThreadReader(NULL),
|
|
m_hThreadWorker(NULL),
|
|
m_hevtWorker(NULL),
|
|
m_hevtReader(NULL),
|
|
m_bReadEnabled(false),
|
|
m_bTerminateWorker(false),
|
|
m_bTerminateReader(false),
|
|
m_pipeList(NULL),
|
|
m_bcsPipeListInitialized(false),
|
|
m_cNtLog( pwszLogFileName )
|
|
{
|
|
//
|
|
// Initialize the general message types array and install the message
|
|
// handlers.
|
|
//
|
|
InitMsgTypes();
|
|
InstallServerMsgHandlers();
|
|
}
|
|
|
|
// free data allocated by the class
|
|
void CVsTstMsgHandler::FreeData()
|
|
{
|
|
if (m_bcsQueueInitialized)
|
|
{
|
|
m_csQueue.Term();
|
|
m_bcsQueueInitialized = false;
|
|
}
|
|
|
|
if (m_bcsPipeListInitialized)
|
|
{
|
|
m_csPipeList.Term();
|
|
m_bcsPipeListInitialized = false;
|
|
}
|
|
|
|
while(m_pmsgFirst)
|
|
{
|
|
VSTST_MSG_HDR *pmsgNext = m_pmsgFirst->pmsgNext;
|
|
delete m_pmsgFirst;
|
|
m_pmsgFirst = pmsgNext;
|
|
}
|
|
|
|
if (m_hThreadWorker)
|
|
{
|
|
CloseHandle(m_hThreadWorker);
|
|
m_hThreadWorker = NULL;
|
|
}
|
|
|
|
if (m_hevtWorker)
|
|
{
|
|
CloseHandle(m_hevtWorker);
|
|
m_hevtWorker = NULL;
|
|
}
|
|
|
|
if (m_hevtReader)
|
|
{
|
|
CloseHandle(m_hevtReader);
|
|
m_hevtReader = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
CVsTstMsgHandler::~CVsTstMsgHandler()
|
|
{
|
|
ForceTermination();
|
|
|
|
FreeData();
|
|
}
|
|
|
|
|
|
// initailize message handler and worker thread
|
|
HRESULT CVsTstMsgHandler::Initialize(UINT cbMaxMsg)
|
|
{
|
|
m_cbMaxMsgLength = cbMaxMsg;
|
|
try
|
|
{
|
|
m_csQueue.Init();
|
|
m_bcsQueueInitialized = true;
|
|
m_csPipeList.Init();
|
|
m_bcsPipeListInitialized = true;
|
|
}
|
|
catch(...)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
m_hevtWorker = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (m_hevtWorker == NULL)
|
|
goto _ErrExit;
|
|
|
|
m_hevtReader = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (m_hevtReader == NULL)
|
|
goto _ErrExit;
|
|
|
|
DWORD tid;
|
|
|
|
m_hThreadWorker = CreateThread
|
|
(
|
|
NULL,
|
|
64*1024,
|
|
StartWorkerThread,
|
|
this,
|
|
0,
|
|
&tid
|
|
);
|
|
|
|
|
|
if (m_hThreadWorker == NULL)
|
|
goto _ErrExit;
|
|
|
|
return S_OK;
|
|
|
|
_ErrExit:
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
FreeData();
|
|
return hr;
|
|
}
|
|
|
|
|
|
// adjust message pointers
|
|
bool CVsTstMsgHandler::AdjustPointers(VSTST_MSG_HDR *phdr)
|
|
{
|
|
VSTST_MSG_TYPE_TABLE *pType = &g_msgTypes[phdr->type];
|
|
BYTE *pb = phdr->rgb;
|
|
VOID **ppv = (VOID **) (pb + pType->cbFixed);
|
|
pb = (BYTE *) (ppv + pType->cVarPtr);
|
|
for(unsigned iVarPtr = 0; iVarPtr < pType->cVarPtr; iVarPtr++, ppv++)
|
|
{
|
|
*ppv = pb;
|
|
size_t cb;
|
|
switch(pType->pointerTypes[iVarPtr])
|
|
{
|
|
default:
|
|
return false;
|
|
|
|
case VSTST_VPT_BYTE:
|
|
cb = *(UINT *) pb;
|
|
break;
|
|
|
|
case VSTST_VPT_ANSI:
|
|
cb = strlen((char *) pb) + 1;
|
|
break;
|
|
|
|
case VSTST_VPT_UNICODE:
|
|
cb = (wcslen((WCHAR *) pb) + 1) * sizeof(WCHAR);
|
|
break;
|
|
}
|
|
|
|
// align to pointer boundary
|
|
cb = (cb + sizeof(PVOID) - 1) & ~(sizeof(PVOID) - 1);
|
|
pb += cb;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// process message immediately
|
|
bool CVsTstMsgHandler::ProcessMsgImmediate(VSTST_MSG_HDR *phdr)
|
|
{
|
|
if (!AdjustPointers(phdr))
|
|
return false;
|
|
|
|
VSTST_MSG_TYPE_TABLE *pType = &g_msgTypes[phdr->type];
|
|
try
|
|
{
|
|
pType->pfnHandler(phdr, &m_cNtLog);
|
|
}
|
|
catch(...)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// queue message for worker thrad
|
|
bool CVsTstMsgHandler::QueueMsg(VSTST_MSG_HDR *phdr)
|
|
{
|
|
BYTE *pbMsg = new BYTE[phdr->cbMsg];
|
|
if (pbMsg == NULL)
|
|
// can't allocate message, wait for queue to of messages
|
|
// to complete and then process messages serially
|
|
{
|
|
WaitForQueueToComplete();
|
|
return ProcessMsgImmediate(phdr);
|
|
}
|
|
|
|
memcpy(pbMsg, phdr, phdr->cbMsg);
|
|
VSTST_MSG_HDR *phdrT = (VSTST_MSG_HDR *) pbMsg;
|
|
if (!AdjustPointers(phdrT))
|
|
return false;
|
|
|
|
m_csQueue.Lock();
|
|
if (m_pmsgLast == NULL)
|
|
{
|
|
VSTST_ASSERT(m_pmsgFirst == NULL);
|
|
m_pmsgLast = m_pmsgFirst = phdrT;
|
|
SetEvent(m_hevtWorker);
|
|
}
|
|
else
|
|
{
|
|
VSTST_ASSERT(m_pmsgLast->pmsgNext == NULL);
|
|
m_pmsgLast->pmsgNext = phdrT;
|
|
|
|
// replace last element on queue
|
|
m_pmsgLast = phdrT;
|
|
}
|
|
|
|
m_csQueue.Unlock();
|
|
return true;
|
|
}
|
|
|
|
|
|
// execute items on the work queue
|
|
bool CVsTstMsgHandler::DoWork()
|
|
{
|
|
// Add this thread as a participant
|
|
m_cNtLog.AddParticipant();
|
|
|
|
// For now start the one and only variation
|
|
m_cNtLog.StartVariation( L"VssTestController" );
|
|
|
|
while(TRUE)
|
|
{
|
|
// wait for something to get on the work queue or for 1 second
|
|
if (WaitForSingleObject(m_hevtWorker, 1000) == WAIT_FAILED)
|
|
{
|
|
// Log severe error
|
|
m_cNtLog.Log( eSevLev_Severe, L"CVsTstMsgHandler::DoWork, WaitForSingleObject returned WAIT_FAILED, dwRet: %d", ::GetLastError() );
|
|
|
|
// End the variation
|
|
m_cNtLog.EndVariation();
|
|
|
|
// Remove the thread as a participant
|
|
m_cNtLog.RemoveParticipant();
|
|
|
|
return false;
|
|
}
|
|
|
|
while(TRUE)
|
|
{
|
|
// lock queue
|
|
m_csQueue.Lock();
|
|
|
|
// check whether queue is empty
|
|
if (m_pmsgFirst == NULL)
|
|
{
|
|
VSTST_ASSERT(m_pmsgLast == NULL);
|
|
|
|
// check whether we should terminate the thread
|
|
if (m_bTerminateWorker)
|
|
{
|
|
// terminate thread
|
|
m_csQueue.Unlock();
|
|
|
|
// End the variation
|
|
m_cNtLog.EndVariation();
|
|
|
|
// Remove the thread as a participant
|
|
m_cNtLog.RemoveParticipant();
|
|
|
|
return true;
|
|
}
|
|
|
|
// setup to wait again
|
|
ResetEvent(m_hevtWorker);
|
|
m_csQueue.Unlock();
|
|
break;
|
|
}
|
|
|
|
// pull first message off of queue
|
|
VSTST_MSG_HDR *phdr = m_pmsgFirst;
|
|
|
|
// move head of queue to next element
|
|
m_pmsgFirst = m_pmsgFirst->pmsgNext;
|
|
|
|
// is queue now empty
|
|
if (m_pmsgFirst == NULL)
|
|
{
|
|
VSTST_ASSERT(m_pmsgLast == phdr);
|
|
|
|
// set tail of queue to null
|
|
m_pmsgLast = NULL;
|
|
}
|
|
|
|
// unlock queue before executing item
|
|
m_csQueue.Unlock();
|
|
|
|
// execute item
|
|
VSTST_MSG_TYPE_TABLE *pType = &g_msgTypes[phdr->type];
|
|
try
|
|
{
|
|
pType->pfnHandler(phdr, &m_cNtLog );
|
|
}
|
|
catch(...)
|
|
{
|
|
// Log severe error
|
|
m_cNtLog.Log( eSevLev_Severe, L"CVsTstMsgHandler::DoWork, caught unexpected exception from message handler" );
|
|
|
|
// End the variation
|
|
m_cNtLog.EndVariation();
|
|
|
|
// Remove the thread as a participant
|
|
m_cNtLog.RemoveParticipant();
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// terminate worker thread, waiting for all work to complete
|
|
void CVsTstMsgHandler::WaitForQueueToComplete()
|
|
{
|
|
m_bTerminateWorker = true;
|
|
if (WaitForSingleObject(m_hThreadWorker, INFINITE) == WAIT_FAILED)
|
|
{
|
|
// polling way to wait if we wait fails. Note that we usually
|
|
// would only expect to get here in stress situations
|
|
while(TRUE)
|
|
{
|
|
m_csQueue.Lock();
|
|
if (m_pmsgFirst == NULL)
|
|
{
|
|
m_csQueue.Unlock();
|
|
break;
|
|
}
|
|
|
|
m_csQueue.Unlock();
|
|
Sleep(100);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
DWORD CVsTstMsgHandler::StartWorkerThread(VOID *pv)
|
|
{
|
|
CVsTstMsgHandler *pHandler = (CVsTstMsgHandler *) pv;
|
|
|
|
try
|
|
{
|
|
pHandler->DoWork();
|
|
}
|
|
catch(...)
|
|
{
|
|
LogUnexpectedFailure(L"Worker thread unexpectedly terminated");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CVsTstMsgHandler::StartProcessingMessages()
|
|
{
|
|
m_bReadEnabled = true;
|
|
SetEvent(m_hevtReader);
|
|
}
|
|
|
|
void CVsTstMsgHandler::StopProcessingMessages()
|
|
{
|
|
ResetEvent(m_hevtReader);
|
|
m_bReadEnabled = false;
|
|
}
|
|
|
|
void CVsTstMsgHandler::ForceTermination()
|
|
{
|
|
m_bReadEnabled = false;
|
|
m_bTerminateWorker = true;
|
|
m_bTerminateReader = true;
|
|
SetEvent(m_hevtReader);
|
|
if (m_hThreadWorker)
|
|
{
|
|
DWORD dwErr = WaitForSingleObject(m_hThreadWorker, 5000);
|
|
if (dwErr == WAIT_FAILED || dwErr == WAIT_TIMEOUT)
|
|
// force termination of worker thread
|
|
TerminateThread(m_hThreadWorker, 1);
|
|
|
|
CloseHandle(m_hThreadWorker);
|
|
m_hThreadWorker = NULL;
|
|
}
|
|
|
|
m_csPipeList.Lock();
|
|
|
|
while(m_pipeList != NULL)
|
|
{
|
|
CVsTstPipe *pipe = m_pipeList;
|
|
HANDLE hThread = pipe->m_hThreadReader;
|
|
pipe->m_hThreadReader = NULL;
|
|
m_csPipeList.Unlock();
|
|
m_pipeList->ForceTermination(hThread);
|
|
m_csPipeList.Lock();
|
|
if (m_pipeList == pipe)
|
|
delete m_pipeList;
|
|
}
|
|
|
|
m_csPipeList.Unlock();
|
|
}
|
|
|
|
// launch a pipe reader thread
|
|
HRESULT CVsTstMsgHandler::LaunchReader()
|
|
{
|
|
CVsTstPipe *pipe = new CVsTstPipe(this);
|
|
if (pipe == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hr = pipe->Initialize(m_cbMaxMsgLength);
|
|
if (FAILED(hr))
|
|
{
|
|
delete pipe;
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// link pipe into pipe list
|
|
void CVsTstMsgHandler::LinkPipe(CVsTstPipe *pipe)
|
|
{
|
|
VSTST_ASSERT(!pipe->m_bLinked);
|
|
m_csPipeList.Lock();
|
|
pipe->m_prev = NULL;
|
|
pipe->m_next = m_pipeList;
|
|
if (m_pipeList)
|
|
{
|
|
VSTST_ASSERT(m_pipeList->m_prev == NULL);
|
|
m_pipeList->m_prev = pipe;
|
|
}
|
|
|
|
m_pipeList = pipe;
|
|
m_csPipeList.Unlock();
|
|
pipe->m_bLinked = true;
|
|
}
|
|
|
|
// unlink pipe from pipe list
|
|
void CVsTstMsgHandler::UnlinkPipe(CVsTstPipe *pipe)
|
|
{
|
|
VSTST_ASSERT(pipe->m_bLinked);
|
|
m_csPipeList.Lock();
|
|
if (pipe->m_prev == NULL)
|
|
{
|
|
VSTST_ASSERT(m_pipeList == pipe);
|
|
m_pipeList = pipe->m_next;
|
|
if (m_pipeList)
|
|
{
|
|
VSTST_ASSERT(m_pipeList->m_prev == pipe);
|
|
m_pipeList->m_prev = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VSTST_ASSERT(pipe->m_prev->m_next == pipe);
|
|
pipe->m_prev->m_next = pipe->m_next;
|
|
if (pipe->m_next)
|
|
{
|
|
VSTST_ASSERT(pipe->m_next->m_prev == pipe);
|
|
pipe->m_next->m_prev = pipe->m_prev;
|
|
}
|
|
}
|
|
|
|
m_csPipeList.Unlock();
|
|
pipe->m_bLinked = false;
|
|
}
|
|
|
|
|
|
// constructor for a pipe
|
|
CVsTstPipe::CVsTstPipe(CVsTstMsgHandler *pHandler) :
|
|
m_hPipe(NULL),
|
|
m_hevtOverlapped(NULL),
|
|
m_rgbMsg(NULL),
|
|
m_cbMsg(0),
|
|
m_hThreadReader(NULL),
|
|
m_bLinked(NULL),
|
|
m_pHandler(pHandler),
|
|
m_bConnected(false)
|
|
{
|
|
}
|
|
|
|
// destructor for a pipe
|
|
CVsTstPipe::~CVsTstPipe()
|
|
{
|
|
// unlink pipe from list if linked
|
|
if (m_bLinked)
|
|
{
|
|
VSTST_ASSERT(m_pHandler);
|
|
m_pHandler->UnlinkPipe(this);
|
|
}
|
|
|
|
FreeData();
|
|
}
|
|
|
|
|
|
// initailize message handler and worker thread
|
|
HRESULT CVsTstPipe::Initialize(UINT cbMaxMsg)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// create pipe
|
|
m_hPipe = CreateNamedPipe
|
|
(
|
|
s_wszPipeName,
|
|
FILE_FLAG_OVERLAPPED|PIPE_ACCESS_INBOUND,
|
|
PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT,
|
|
PIPE_UNLIMITED_INSTANCES,
|
|
0,
|
|
cbMaxMsg,
|
|
100,
|
|
NULL
|
|
);
|
|
|
|
if (m_hPipe == INVALID_HANDLE_VALUE)
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
// create message buffer
|
|
m_cbMsg = cbMaxMsg;
|
|
m_rgbMsg = new BYTE[m_cbMsg];
|
|
if (m_rgbMsg == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto _ErrCleanup;
|
|
}
|
|
|
|
// create overlapped read event
|
|
m_hevtOverlapped = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (m_hevtOverlapped == NULL)
|
|
goto _ErrExit;
|
|
|
|
|
|
|
|
// create reader thread
|
|
DWORD tid;
|
|
m_hThreadReader = CreateThread
|
|
(
|
|
NULL,
|
|
256*1024,
|
|
StartReaderThread,
|
|
this,
|
|
0,
|
|
&tid
|
|
);
|
|
|
|
if (m_hThreadReader == NULL)
|
|
goto _ErrExit;
|
|
|
|
// link pipe into list
|
|
m_pHandler->LinkPipe(this);
|
|
|
|
return S_OK;
|
|
|
|
_ErrExit:
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
_ErrCleanup:
|
|
FreeData();
|
|
return hr;
|
|
}
|
|
|
|
|
|
// free data allocated by the class
|
|
void CVsTstPipe::FreeData()
|
|
{
|
|
delete m_rgbMsg;
|
|
m_rgbMsg = NULL;
|
|
|
|
if (m_hPipe != INVALID_HANDLE_VALUE)
|
|
{
|
|
// disconnect pipe if connected
|
|
if (m_bConnected)
|
|
{
|
|
DisconnectNamedPipe(m_hPipe);
|
|
m_bConnected = false;
|
|
}
|
|
|
|
CloseHandle(m_hPipe);
|
|
m_hPipe = NULL;
|
|
}
|
|
|
|
if (m_hThreadReader)
|
|
{
|
|
CloseHandle(m_hThreadReader);
|
|
m_hThreadReader = NULL;
|
|
}
|
|
|
|
if (m_hevtOverlapped)
|
|
{
|
|
CloseHandle(m_hevtOverlapped);
|
|
m_hevtOverlapped = NULL;
|
|
}
|
|
}
|
|
|
|
void CVsTstPipe::ForceTermination(HANDLE hThread)
|
|
{
|
|
// thread should already be terminated
|
|
DWORD dwErr = WaitForSingleObject(hThread, 5000);
|
|
if (dwErr == WAIT_FAILED || dwErr == WAIT_TIMEOUT)
|
|
// force termination of thread
|
|
TerminateThread(hThread, 1);
|
|
|
|
CloseHandle(hThread);
|
|
}
|
|
|
|
|
|
|
|
// setup overlapped I/O structure used for reading data
|
|
// from the pipe
|
|
void CVsTstPipe::SetupOverlapped()
|
|
{
|
|
VSTST_ASSERT(m_hevtOverlapped);
|
|
ResetEvent(m_hevtOverlapped);
|
|
memset(&m_overlap, 0, sizeof(m_overlap));
|
|
m_overlap.hEvent = m_hevtOverlapped;
|
|
}
|
|
|
|
bool CVsTstPipe::WaitForConnection()
|
|
{
|
|
SetupOverlapped();
|
|
if (ConnectNamedPipe(m_hPipe, &m_overlap))
|
|
return true;
|
|
|
|
if (GetLastError() == ERROR_IO_PENDING)
|
|
{
|
|
while(TRUE)
|
|
{
|
|
if (!m_pHandler->m_bReadEnabled)
|
|
{
|
|
CancelIo(m_hPipe);
|
|
return false;
|
|
}
|
|
|
|
DWORD dwErr = WaitForSingleObject(m_hevtOverlapped, 1000);
|
|
if (dwErr == WAIT_OBJECT_0)
|
|
break;
|
|
|
|
if (dwErr == WAIT_FAILED)
|
|
{
|
|
Sleep(1000);
|
|
CancelIo(m_hPipe);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// do the work of reading messages
|
|
VSTST_READER_STATUS CVsTstPipe::ReadMessages(bool bConnect)
|
|
{
|
|
if (bConnect)
|
|
{
|
|
if (!WaitForConnection())
|
|
return VSTST_RS_NOTCONNECTED;
|
|
|
|
// launch a new reader thread to wait for the next connection
|
|
m_pHandler->LaunchReader();
|
|
}
|
|
|
|
// while we are doing reads
|
|
while(m_pHandler->m_bReadEnabled)
|
|
{
|
|
DWORD cbRead;
|
|
|
|
// setup overlapped structure
|
|
SetupOverlapped();
|
|
|
|
if (!ReadFile
|
|
(
|
|
m_hPipe,
|
|
m_rgbMsg,
|
|
m_cbMsg,
|
|
&cbRead,
|
|
&m_overlap
|
|
))
|
|
{
|
|
DWORD dwErr = GetLastError();
|
|
if (dwErr == ERROR_IO_PENDING)
|
|
{
|
|
while(TRUE)
|
|
{
|
|
if (!m_pHandler->m_bReadEnabled)
|
|
{
|
|
CancelIo(m_hPipe);
|
|
return VSTST_RS_READDISABLED;
|
|
}
|
|
|
|
DWORD dwErr = WaitForSingleObject(m_hevtOverlapped, 1000);
|
|
if (dwErr == WAIT_OBJECT_0)
|
|
break;
|
|
|
|
if (dwErr == WAIT_FAILED)
|
|
{
|
|
Sleep(1000);
|
|
CancelIo(m_hPipe);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!GetOverlappedResult(m_hPipe, &m_overlap, &cbRead, FALSE))
|
|
{
|
|
CancelIo(m_hPipe);
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// unexpected error reading from pipe
|
|
DWORD dwErr = GetLastError();
|
|
if (dwErr == ERROR_BROKEN_PIPE)
|
|
{
|
|
// terminate thread as we are no longer reading from
|
|
// the pipe
|
|
DisconnectNamedPipe(m_hPipe);
|
|
return VSTST_RS_DISCONNECTED;
|
|
}
|
|
else
|
|
ReadPipeError(dwErr);
|
|
|
|
return VSTST_RS_ERROR;
|
|
}
|
|
}
|
|
|
|
VSTST_MSG_HDR *phdr = (VSTST_MSG_HDR *) m_rgbMsg;
|
|
if (phdr->cbMsg != cbRead ||
|
|
phdr->type == VSTST_MT_UNDEFINED ||
|
|
phdr->type >= VSTST_MT_MAXMSGTYPE)
|
|
LogInvalidMessage(phdr);
|
|
else if (g_msgTypes[phdr->type].priority == VSTST_MP_IMMEDIATE)
|
|
{
|
|
if (!m_pHandler->ProcessMsgImmediate(phdr))
|
|
LogInvalidMessage(phdr);
|
|
}
|
|
else
|
|
{
|
|
if (!m_pHandler->QueueMsg(phdr))
|
|
LogInvalidMessage(phdr);
|
|
}
|
|
}
|
|
|
|
return VSTST_RS_READDISABLED;
|
|
}
|
|
|
|
|
|
DWORD CVsTstPipe::StartReaderThread(VOID *pv)
|
|
{
|
|
CVsTstPipe *pipe = (CVsTstPipe *) pv;
|
|
|
|
bool bConnected = false;
|
|
try
|
|
{
|
|
while(!pipe->m_pHandler->m_bTerminateReader)
|
|
{
|
|
if (WaitForSingleObject(pipe->m_pHandler->m_hevtReader, INFINITE) == WAIT_FAILED)
|
|
break;
|
|
|
|
VSTST_READER_STATUS status = pipe->ReadMessages(!bConnected);
|
|
if (status == VSTST_RS_DISCONNECTED)
|
|
{
|
|
bConnected = false;
|
|
break;
|
|
}
|
|
else if (status != VSTST_RS_NOTCONNECTED)
|
|
bConnected = true;
|
|
}
|
|
}
|
|
catch(...)
|
|
{
|
|
LogUnexpectedFailure(L"Read thread unexpectedly terminated");
|
|
}
|
|
|
|
delete pipe;
|
|
return 0;
|
|
}
|
|
|