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.
 
 
 
 
 
 

699 lines
27 KiB

/*****************************************************************************
* (C) COPYRIGHT MICROSOFT CORPORATION, 2002
*
* AUTHOR: ByronC
*
* DATE: 3/30/2002
*
* @doc INTERNAL
*
* @module WiaEventReceiver.cpp - Declarations for <c WiaEventReceiver> |
*
* This file contains the implementation for the <c WiaEventReceiver> class.
*
*****************************************************************************/
#include "cplusinc.h"
#include "coredbg.h"
/*****************************************************************************
* @doc INTERNAL
*
* @mfunc | WiaEventReceiver | WiaEventReceiver |
*
* We initialize all member variables. In general, this sets the values to 0,
* except:
* <nl><md WiaEventReceiver::m_ulSig> is set to be WiaEventReceiver_UNINIT_SIG.
* <nl><md WiaEventReceiver::m_cRef> is set to be 1.
* <nl><md WiaEventReceiver::m_pClientEventTransport> is set to be <p pClientEventTransport>.
*
* @parm ClientEventTransport* | pClientEventTransport |
* The transport class used to communicate with the WIA Service.
*
*****************************************************************************/
WiaEventReceiver::WiaEventReceiver(
ClientEventTransport *pClientEventTransport) :
m_ulSig(WiaEventReceiver_UNINIT_SIG),
m_cRef(1),
m_pClientEventTransport(pClientEventTransport),
m_hEventThread(NULL),
m_bIsRunning(FALSE),
m_dwEventThreadID(0)
{
}
/*****************************************************************************
* @doc INTERNAL
*
* @mfunc | WiaEventReceiver | ~WiaEventReceiver |
*
* Do any cleanup that is not already done.
* <nl>- Call <mf WiaEventReceiver::Stop>
* <nl>- Delete <mf WiaEventReceiver::m_pClientEventTransport>
* <nl>- Call <mf WiaEventReceiver::DestroyRegistrationList>
*
* Also:
* <nl><md WiaEventReceiver::m_ulSig> is set to be WiaEventReceiver_DEL_SIG.
*
*****************************************************************************/
WiaEventReceiver::~WiaEventReceiver()
{
m_ulSig = WiaEventReceiver_DEL_SIG;
m_cRef = 0;
Stop();
if (m_pClientEventTransport)
{
delete m_pClientEventTransport;
m_pClientEventTransport = NULL;
}
DestroyRegistrationList();
}
/*****************************************************************************
* @doc INTERNAL
*
* @mfunc HRESULT | WiaEventReceiver | Start |
*
* This method does any initialization steps needed to start receiving
* notifications from the WIA Service. We:
* <nl>- Open our connection to the server
* <nl>- Open our notification channel
* <nl>- Create our event thread
*
* This method is idempotent i.e. you can call <mf WiaEventReceiver::Start> multiple times safely
* before calling <mf WiaEventReceiver::Stop>. Subsequent calls to <mf WiaEventReceiver::Start>
* have no effect until <mf WiaEventReceiver::Stop> has been called.
*
* @rvalue S_OK |
* This object initialized correctly.
* @rvalue E_XXXXXXXXX |
* This object could not be initialized correctly. It should be released
* immediately.
*****************************************************************************/
HRESULT WiaEventReceiver::Start()
{
HRESULT hr = S_OK;
if (m_csReceiverSync.IsInitialized())
{
TAKE_CRIT_SECT t(m_csReceiverSync);
//
// We put an exception handler around our code to ensure that the
// crtitical section is exited properly.
//
_try
{
if (!m_bIsRunning)
{
if (m_pClientEventTransport)
{
hr = m_pClientEventTransport->Initialize();
if (hr == S_OK)
{
hr = m_pClientEventTransport->OpenConnectionToServer();
if (hr == S_OK)
{
hr = m_pClientEventTransport->OpenNotificationChannel();
if (hr == S_OK)
{
//
// Create our event thread with will wait for
// notifications from the transport layer.
//
m_hEventThread = CreateThread(NULL,
0,
WiaEventReceiver::EventThreadProc,
this,
0,
&m_dwEventThreadID);
if (m_hEventThread)
{
DBG_TRC(("WiaEventReceiver Started..."));
m_bIsRunning = TRUE;
hr = S_OK;
}
else
{
hr = E_UNEXPECTED;
}
}
else
{
hr = E_UNEXPECTED;
}
}
else
{
hr = E_UNEXPECTED;
}
}
else
{
DBG_ERR(("Runtime event Error: Could not initialize the transport for the WiaEventReceiver"));
}
}
else
{
DBG_ERR(("Runtime event Error: The transport for the WiaEventReceiver is NULL"));
hr = E_UNEXPECTED;
}
}
else
{
hr = S_OK;
}
}
_except(EXCEPTION_EXECUTE_HANDLER)
{
DBG_ERR(("Runtime event Error: We caught exception 0x%08X trying to sart receiving notifications", GetExceptionCode()));
hr = E_UNEXPECTED;
// TBD: Rethrow the exception?
}
}
else
{
DBG_ERR(("Runtime event Error: The critical section for the WiaEventReceiver could not be initialized"));
hr = E_UNEXPECTED;
}
if (FAILED(hr))
{
DBG_ERR(("Runtime event Error: Could not Start...calling Stop"));
Stop();
}
return hr;
}
/*****************************************************************************
* @doc INTERNAL
*
* @mfunc HRESULT | WiaEventReceiver | NotifyCallbacksOfEvent |
*
* Walks down the list of event registrations. For every event
* registration that matches <p pWiaEventInfo>, we make the callback.
* (See <mf EventRegistrationInfo::MatchesDeviceEvent> for information on what consitutes
* a match). This is done synchronously.
*
* The callbacks are done in two steps:
* <nl>1) Walk the list to find matching registrations. For each matching registration,
* add it to a ListOfCallbacks (This is done holding the critical section)
* <nl>2) Walk the ListOfCallbacks and make the callback (This is done without holding the critical
* section to avoid deadlock)
*
* @parm WiaEventInfo* | pWiaEventInfo |
* The actual event that occured.
*
* @rvalue S_OK |
* The method succeeded.
*****************************************************************************/
HRESULT WiaEventReceiver::NotifyCallbacksOfEvent(
WiaEventInfo *pWiaEventInfo)
{
CSimpleLinkedList<ClientEventRegistrationInfo*> ListOfCallbacksToNotify;
HRESULT hr = S_OK;
if (pWiaEventInfo)
{
TAKE_CRIT_SECT t(m_csReceiverSync);
//
// We put an exception handler around our code to ensure that the
// crtitical section is exited properly.
//
_try
{
//
// Walk the list and see if we can find it
//
ClientEventRegistrationInfo *pEventRegistrationInfo = NULL;
CSimpleLinkedList<ClientEventRegistrationInfo*>::Iterator iter;
for (iter = m_ListOfEventRegistrations.Begin(); iter != m_ListOfEventRegistrations.End(); ++iter)
{
pEventRegistrationInfo = *iter;
if (pEventRegistrationInfo)
{
//
// Check whether a given registration matches the event. If it does, it means
// we have to ad it to the list of callbacks to notify.
//
if (pEventRegistrationInfo->MatchesDeviceEvent(pWiaEventInfo->getDeviceID(),
pWiaEventInfo->getEventGuid()))
{
//
// AddRef the EventRegistrationInfo because we're keeping it in another list.
// It will be released once the callback is made.
//
ListOfCallbacksToNotify.Append(pEventRegistrationInfo);
pEventRegistrationInfo->AddRef();
}
}
else
{
//
// Log Error
// pEventRegistrationInfo should never be NULL
DBG_ERR(("Runtime event Error: While searching for a matching registration, we hit a NULL pEventRegistrationInfo!"));
}
}
}
_except(EXCEPTION_EXECUTE_HANDLER)
{
DBG_ERR(("Runtime event Error: We caught exception 0x%08X trying to notify callbacks of event", GetExceptionCode()));
hr = E_UNEXPECTED;
}
}
else
{
DBG_ERR(("Runtime event Error: Cannot process NULL event info"));
hr = E_POINTER;
}
//
// We now have a list of callbacks we need to notify of the event. We do this here
// now that we are not holding the Critical Section (m_csReceiver) anymore.
//
if (SUCCEEDED(hr) && pWiaEventInfo)
{
//
// Walk the ListOfCallbacksToNotify and make the callbacks
//
ClientEventRegistrationInfo *pEventRegistrationInfo = NULL;
CSimpleLinkedList<ClientEventRegistrationInfo*>::Iterator iter;
for (iter = ListOfCallbacksToNotify.Begin(); iter != ListOfCallbacksToNotify.End(); ++iter)
{
pEventRegistrationInfo = *iter;
if (pEventRegistrationInfo)
{
//
// Put an exception handler around the actual callback attempt
//
_try
{
GUID guidEvent = pWiaEventInfo->getEventGuid();
ULONG ulEventType = pWiaEventInfo->getEventType();
IWiaEventCallback *pIWiaEventCallback = pEventRegistrationInfo->getCallbackInterface();
if (pIWiaEventCallback)
{
HRESULT hres = pIWiaEventCallback->ImageEventCallback(
&guidEvent,
pWiaEventInfo->getEventDescription(),
pWiaEventInfo->getDeviceID(),
pWiaEventInfo->getDeviceDescription(),
pWiaEventInfo->getDeviceType(),
pWiaEventInfo->getFullItemName(),
&ulEventType,
0);
pIWiaEventCallback->Release();
}
else
{
DBG_WRN(("Cannot notify a NULL IWiaEventCallback"));
}
}
_finally
{
//
// Always release the pEventRegistrationInfo since we AddRef'd it putting it in the list
//
pEventRegistrationInfo->Release();
}
}
}
}
return hr;
}
/*****************************************************************************
* @doc INTERNAL
*
* @mfunc HRESULT | WiaEventReceiver | SendRegisterUnregisterInfo |
*
* This method will add/remove the relevant event registration, then send
* the info off to the WIA Service.
*
* To insert into our list, we grab the <md WiaEventReceiver::m_csReceiverSync>,
* then create a new <c ClientEventRegistrationInfo> class (if this is a registration),
* or we find the registration in the list and remove it (if this is an
* unregister). The sync is released after this.
*
* Only once this is done, do we notify the service.
*
* This method will automatically ensure we are started by calling <mf WiaEventReceiver::Start>.
* If at the end of this method, there are no registrations, it will call
* <mf WiaEventReceiver::Stop>. (Because, if the client
* is not registered for anything, we do not need our event thread or an active channel
* to the server).
*
* @parm ClientEventRegistrationInfo* | pEventRegistrationInfo |
* Pointer to a class containing the registration data to add/remove.
*
* @rvalue S_OK |
* The method succeeded.
* @rvalue E_XXXXXXX |
* We could not send the register/unregister info.
*****************************************************************************/
HRESULT WiaEventReceiver::SendRegisterUnregisterInfo(
ClientEventRegistrationInfo *pEventRegistrationInfo)
{
HRESULT hr = S_OK;
if (pEventRegistrationInfo)
{
//
// Ensure we have a channel open to the server and then send the registration info.
//
Start();
hr = m_pClientEventTransport->SendRegisterUnregisterInfo(pEventRegistrationInfo);
}
else
{
hr = E_UNEXPECTED;
}
//
// Ensure we take the sync for the rest of this function
//
if (SUCCEEDED(hr))
{
TAKE_CRIT_SECT t(m_csReceiverSync);
//
// We put an exception handler around our code to ensure that the
// crtitical section is exited properly.
//
_try
{
//
// Check whether this is registration or unregistration.
// NOTE: Since unregistration is typically done via the RegistrationCookie,
// our hueristic for this is that if it is not specifically an UnRegistration,
// then it is considered a registration.
//
if (pEventRegistrationInfo->getFlags() & WIA_UNREGISTER_EVENT_CALLBACK)
{
ClientEventRegistrationInfo *pExistingReg = FindEqualEventRegistration(pEventRegistrationInfo);
if (pExistingReg != NULL)
{
//
// Release it and remove it from our list.
//
m_ListOfEventRegistrations.Remove(pExistingReg);
pExistingReg->Release();
DBG_TRC(("Removed registration:"));
pExistingReg->Dump();
}
else
{
DBG_ERR(("Runtime event Error: Attempting to unregister when you have not first registered"));
hr = E_INVALIDARG;
}
//
// We need to release pExistingReg due to the AddRef from the lookup.
//
if (pExistingReg)
{
pExistingReg->Release();
pExistingReg = NULL;
}
}
else
{
//
// Add it to our list. We AddRef it here since we're keeping a reference to it.
//
m_ListOfEventRegistrations.Prepend(pEventRegistrationInfo);
pEventRegistrationInfo->AddRef();
DBG_TRC(("Added new registration:"));
pEventRegistrationInfo->Dump();
}
//
// If no registrations exit in the list, then we should stop.
// If we have at least one registration in the list, then we should start.
//
if (m_ListOfEventRegistrations.Empty())
{
Stop();
}
}
_except(EXCEPTION_EXECUTE_HANDLER)
{
DBG_ERR(("Runtime event Error: We caught exception 0x%08X trying to register/unregister for event", GetExceptionCode()));
hr = E_UNEXPECTED;
}
}
//*/
return hr;
}
/*****************************************************************************
* @doc INTERNAL
*
* @mfunc VOID | WiaEventReceiver | Stop |
*
* Calls transport object to:
* <nl>- Close our notification channel. See <mf ClientEventTransport::CloseNotificationChannel>
* <nl>- Close our connection to the server. See <mf ClientEventTransport::CloseConnectionToServer>
* <nl>- Close our event thread handle.
* <nl>- Set <md WiaEventReceiver::m_bIsRunning> to FALSE.
*
*****************************************************************************/
VOID WiaEventReceiver::Stop()
{
if (m_csReceiverSync.IsInitialized())
{
TAKE_CRIT_SECT t(m_csReceiverSync);
//
// We put an exception handler around our code to ensure that the
// crtitical section is exited properly.
//
_try
{
if (m_bIsRunning)
{
DBG_TRC(("...WiaEventReceiver is Stopping..."));
m_bIsRunning = FALSE;
m_dwEventThreadID = 0;
if (m_pClientEventTransport)
{
m_pClientEventTransport->CloseNotificationChannel();
m_pClientEventTransport->CloseConnectionToServer();
}
if (m_hEventThread)
{
CloseHandle(m_hEventThread);
m_hEventThread = NULL;
}
}
}
_except(EXCEPTION_EXECUTE_HANDLER)
{
DBG_ERR(("Runtime event Error: We caught exception 0x%08X trying to Stop", GetExceptionCode()));
}
}
}
/*****************************************************************************
* @doc INTERNAL
*
* @mfunc VOID | WiaEventReceiver | DestroyRegistrationList |
*
* Removes any remaining event registration objects and destroys the list.
*
*****************************************************************************/
VOID WiaEventReceiver::DestroyRegistrationList()
{
TAKE_CRIT_SECT t(m_csReceiverSync);
//
// We put an exception handler around our code to ensure that the
// crtitical section is exited properly.
//
_try
{
//
// Walk the list of registrations release all elements. Then destroy the list.
//
ClientEventRegistrationInfo *pElem = NULL;
CSimpleLinkedList<ClientEventRegistrationInfo*>::Iterator iter;
for (iter = m_ListOfEventRegistrations.Begin(); iter != m_ListOfEventRegistrations.End(); ++iter)
{
pElem = *iter;
if (pElem)
{
pElem->Release();
}
}
m_ListOfEventRegistrations.Destroy();
}
_except(EXCEPTION_EXECUTE_HANDLER)
{
DBG_ERR(("Runtime event Error: We caught exception 0x%08X trying to destroy the registration list", GetExceptionCode()));
}
}
/*****************************************************************************
* @doc INTERNAL
*
* @mfunc ClientEventRegistrationInfo* | WiaEventReceiver | FindEqualEventRegistration |
*
* Checks whether a semantically equal <c ClientEventRegistrationInfo> is in the list.
* If it is, we retrieve it. Note that caller must Release it.
*
* @parm ClientEventRegistrationInfo | pEventRegistrationInfo |
* Specifies a <c ClientEventRegistrationInfo> we're looking for in our list.
*
* @rvalue NULL |
* We could not find it.
* @rvalue non-NULL |
* The equivalent <c ClientEventRegistrationInfo> exists. Caller must Release.
*****************************************************************************/
ClientEventRegistrationInfo* WiaEventReceiver::FindEqualEventRegistration(
ClientEventRegistrationInfo *pEventRegistrationInfo)
{
ClientEventRegistrationInfo *pRet = NULL;
if (pEventRegistrationInfo)
{
TAKE_CRIT_SECT t(m_csReceiverSync);
//
// We put an exception handler around our code to ensure that the
// crtitical section is exited properly.
//
_try
{
//
// Walk the list and see if we can find it
//
ClientEventRegistrationInfo *pElem = NULL;
CSimpleLinkedList<ClientEventRegistrationInfo*>::Iterator iter;
for (iter = m_ListOfEventRegistrations.Begin(); iter != m_ListOfEventRegistrations.End(); ++iter)
{
pElem = *iter;
if (pElem)
{
if (pElem->Equals(pEventRegistrationInfo))
{
//
// We found it, so AddRef it and set the return
//
pElem->AddRef();
pRet = pElem;
break;
}
}
else
{
//
// Log Error
// pEventRegistrationInfo should never be NULL
DBG_ERR(("Runtime event Error: While searching for an equal registration, we hit a NULL pEventRegistrationInfo!"));
}
}
}
_except(EXCEPTION_EXECUTE_HANDLER)
{
DBG_ERR(("Runtime event Error: The WiaEventReceiver caught an exception (0x%08X) trying to find an equal event registration", GetExceptionCode()));
}
}
return pRet;
}
/*****************************************************************************
* @doc INTERNAL
*
* @mfunc DWORD WINAPI | WiaEventReceiver | EventThreadProc |
*
* This method waits on the handle returned by <mf ClientEventTransport::getNotificationHandle>.
* When this event is signalled, it means we either have a notification, or we are
* being asked to exit.
*
* Before processing the event, we check whether we are being asked to exit by
* checking the <md WiaEventReceiver::m_dwEventThreadID> member. If it is not
* equal to our thread ID, then we must exit this thread.
*
* @parm LPVOID | lpParameter |
* This is actually a pointer to the instance of <c WiaEventReceiver>.
*
* @rvalue S_OK |
* The method succeeded.
*****************************************************************************/
DWORD WINAPI WiaEventReceiver::EventThreadProc(
LPVOID lpParameter)
{
WiaEventReceiver *pThis = (WiaEventReceiver*)lpParameter;
BOOL bRunning = TRUE;
if (pThis)
{
//
// Call CoInitializeEx here. We prefer multithreaded but we will
// work with any aprtment model.
//
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (pThis->m_pClientEventTransport)
{
HANDLE hEvent = pThis->m_pClientEventTransport->getNotificationHandle();
RPC_STATUS rpcStatus = RPC_S_OK;
while (bRunning)
{
DWORD dwWait = WaitForSingleObject(hEvent, INFINITE);
if (dwWait == WAIT_OBJECT_0)
{
//
// Check whether we are still supposed to be running.
// We are supposed to be running if our thread id matches the
// one in the event receiver. If not, then we must exit.
//
if (pThis->m_dwEventThreadID == GetCurrentThreadId())
{
DBG_TRC(("...We got the event. Retriving data"));
WiaEventInfo wEventInfo;
rpcStatus = pThis->m_pClientEventTransport->FillEventData(&wEventInfo);
if (rpcStatus == RPC_S_OK)
{
HRESULT hrRes = pThis->NotifyCallbacksOfEvent(&wEventInfo);
}
else
{
DBG_WRN(("We got an error 0x%08X trying to fill the event data.", rpcStatus));
//
// TBD: Should we reset our connection to the server?
// Let's try to reconnect... If that fails, then just stop.
//
DBG_WRN(("Resetting connection to server"));
pThis->Stop();
rpcStatus = pThis->Start();
if (rpcStatus != RPC_S_OK)
{
DBG_WRN(("Resetting connection to server failed with 0x%08X, closing our connection", rpcStatus));
pThis->Stop();
}
break;
}
}
else
{
DBG_TRC(("!Received notification to Shutdown event thread!"));
bRunning = FALSE;
}
}
}
}
else
{
DBG_ERR(("Cannot work with a NULL event transport"));
}
}
else
{
DBG_ERR(("Cannot work with a NULL WiaEventReceiver"));
}
CoUninitialize();
DBG_TRC(("\nEvent Thread 0x%08X is now shutdown\n", GetCurrentThreadId()));
return 0;
}