/******************************************************************************* * SPEventQ.h * *------------* * Description: * This is the header file for the SAPI5 event queue implementation. *------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. *******************************************************************************/ #ifndef SPEventQ_h #define SPEventQ_h #ifndef SPHelper_h #include #endif #ifndef SPCollec_h #include #endif //=== Inline helpers for copying and deleting events ============================ //=== Class definition ========================================================== class CSpEventNode : public CSpEvent { public: CSpEventNode * m_pNext; static LONG Compare(const CSpEventNode * p1, const CSpEventNode *p2) { // Assumes offsets DO or DO NOT reset when stream number changes if (p1->ulStreamNum < p2->ulStreamNum) { return -1; } else if (p1->ulStreamNum > p2->ulStreamNum) { return 1; } else if (p1->ullAudioStreamOffset < p2->ullAudioStreamOffset) { return -1; } else if (p1->ullAudioStreamOffset > p2->ullAudioStreamOffset) { return 1; } return 0; } }; typedef CSpBasicQueue CSpEventList; #define DECLARE_SPNOTIFYSOURCE_METHODS(T) \ STDMETHODIMP SetNotifySink(ISpNotifySink * pNotifySink) \ { return T._SetNotifySink(pNotifySink); } \ STDMETHODIMP SetNotifyWindowMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) \ { return T._SetNotifyWindowMessage(hWnd, Msg, wParam, lParam); } \ STDMETHODIMP SetNotifyCallbackFunction(SPNOTIFYCALLBACK * pfnCallback, WPARAM wParam, LPARAM lParam) \ { return T._SetNotifyCallbackFunction(pfnCallback, wParam, lParam); } \ STDMETHODIMP SetNotifyCallbackInterface(ISpNotifyCallback * pSpCallback, WPARAM wParam, LPARAM lParam) \ { return T._SetNotifyCallbackInterface(pSpCallback, wParam, lParam); } \ STDMETHODIMP SetNotifyWin32Event() \ { return T._SetNotifyWin32Event(); } \ STDMETHODIMP WaitForNotifyEvent(DWORD dwMilliseconds) \ { return T._WaitForNotifyEvent(dwMilliseconds); } \ STDMETHODIMP_(HANDLE) GetNotifyEventHandle() \ { return T._GetNotifyEventHandle(); } #define DECLARE_SPEVENTSOURCE_METHODS(T) \ DECLARE_SPNOTIFYSOURCE_METHODS(T) \ STDMETHODIMP SetInterest(ULONGLONG ullEventInterest, ULONGLONG ullQueuedInterest) \ { return T._SetInterest(ullEventInterest, ullQueuedInterest); } \ STDMETHODIMP GetEvents(ULONG ulCount, SPEVENT* pEventArray, ULONG * pulFetched) \ { return T._GetEvents(ulCount, pEventArray, pulFetched); } \ STDMETHODIMP GetInfo(SPEVENTSOURCEINFO *pInfo) \ { return T._GetInfo(pInfo); } class CSpEventSource { public: CSpEventSource(CComObjectRootEx * pParent) : m_pParent(pParent) { m_ullEventInterest = 0; m_ullQueuedInterest = 0; m_ulStreamNum = 0; } HRESULT _SetNotifySink(ISpNotifySink * pNotifySink); HRESULT _SetNotifyWindowMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); HRESULT _SetNotifyCallbackFunction(SPNOTIFYCALLBACK * pfnCallback, WPARAM wParam, LPARAM lParam); HRESULT _SetNotifyCallbackInterface(ISpNotifyCallback * pSpCallback, WPARAM wParam, LPARAM lParam); HRESULT _SetNotifyWin32Event(); HRESULT _WaitForNotifyEvent(DWORD dwMilliseconds); HANDLE _GetNotifyEventHandle(); HRESULT _SetInterest(ULONGLONG ullEventInterest , ULONGLONG ullQueuedInterest); HRESULT _GetEvents( ULONG ulCount, SPEVENT* pEventArray, ULONG * pulFetched ); HRESULT _GetInfo(SPEVENTSOURCEINFO *pInfo ); /*--- Non interface methods ---*/ HRESULT _CompleteEvents( ULONGLONG ullPos = 0xFFFFFFFFFFFFFFFF ); inline void _MoveAllToFreeList(CSpEventList * pList); inline void _RemoveAllEvents(); inline HRESULT _AddEvent(const SPEVENT & Event); inline HRESULT _AddEvents(const SPEVENT* pEventArray, ULONG ulCount); inline HRESULT _DeserializeAndAddEvent(const BYTE * pBuffer, ULONG * pcbUsed); inline HRESULT _GetStreamNumber(const ULONGLONG ullAudioOffset, ULONG *pulStreamNum); //=== Data members ============================== public: ULONGLONG m_ullEventInterest; ULONGLONG m_ullQueuedInterest; ULONG m_ulStreamNum; CSpEventList m_PendingList; CSpEventList m_CompletedList; CSpEventList m_FreeList; CComPtr m_cpNotifySink; CComPtr m_cpEventTranslator; // If non-NULL then Win32 events being used CComObjectRootEx * m_pParent; CComAutoCriticalSection m_NotifyObjChangeCrit; // Critical section used to make sure that // the notify object (m_cpNotifySink) not changed // while waiting on it. }; // //=== Inlines ========================================================= // // // WARNING: If this logic changes, you will need to change the logic in SetNotifyWin32Event also. // inline HRESULT CSpEventSource::_SetNotifySink(ISpNotifySink * pNotifySink) { if (SP_IS_BAD_OPTIONAL_INTERFACE_PTR(pNotifySink)) { return E_INVALIDARG; } else { m_pParent->Lock(); m_NotifyObjChangeCrit.Lock(); m_cpEventTranslator.Release(); m_cpNotifySink = pNotifySink; if (m_cpNotifySink && m_CompletedList.GetHead()) { m_cpNotifySink->Notify(); } m_NotifyObjChangeCrit.Unlock(); m_pParent->Unlock(); return S_OK; } } /**************************************************************************** * CSpEventSource::_SetNotifyWindowMessage * *-----------------------------------------* * Description: * * Returns: * ********************************************************************* RAL ***/ inline HRESULT CSpEventSource::_SetNotifyWindowMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { SPDBG_FUNC("CSpEventSource::_SetNotifyWindowMessage"); HRESULT hr = S_OK; CComPtr cpTranslator; hr = cpTranslator.CoCreateInstance(CLSID_SpNotifyTranslator); if (SUCCEEDED(hr)) { hr = cpTranslator->InitWindowMessage(hWnd, Msg, wParam, lParam); } if (SUCCEEDED(hr)) { hr = _SetNotifySink(cpTranslator); } return hr; } /**************************************************************************** * CSpEventSource::_SetNotifyCallbackFunction * *--------------------------------------------* * Description: * * Returns: * ********************************************************************* RAL ***/ inline HRESULT CSpEventSource::_SetNotifyCallbackFunction(SPNOTIFYCALLBACK * pfnCallback, WPARAM wParam, LPARAM lParam) { SPDBG_FUNC("CSpEventSource::_SetNotifyCallbackFunction"); HRESULT hr = S_OK; CComPtr cpTranslator; hr = cpTranslator.CoCreateInstance(CLSID_SpNotifyTranslator); if (SUCCEEDED(hr)) { hr = cpTranslator->InitCallback(pfnCallback, wParam, lParam); } if (SUCCEEDED(hr)) { hr = _SetNotifySink(cpTranslator); } return hr; } /**************************************************************************** * CSpEventSource::_SetNotifyCallbackInterface * *---------------------------------------------* * Description: * * Returns: * ********************************************************************* RAL ***/ inline HRESULT CSpEventSource::_SetNotifyCallbackInterface(ISpNotifyCallback * pSpCallback, WPARAM wParam, LPARAM lParam) { SPDBG_FUNC("CSpEventSource::_SetNotifyCallbackInterface"); HRESULT hr = S_OK; CComPtr cpTranslator; hr = cpTranslator.CoCreateInstance(CLSID_SpNotifyTranslator); if (SUCCEEDED(hr)) { hr = cpTranslator->InitSpNotifyCallback(pSpCallback, wParam, lParam); } if (SUCCEEDED(hr)) { hr = _SetNotifySink(cpTranslator); } return hr; } /**************************************************************************** * CSpEventSource::_SetNotifyWin32Event * *--------------------------------------* * Description: * * Returns: * ********************************************************************* RAL ***/ inline HRESULT CSpEventSource::_SetNotifyWin32Event(void) { SPDBG_FUNC("CSpEventSource::_SetNotifyWin32Event"); HRESULT hr = S_OK; CComPtr cpTranslator; hr = cpTranslator.CoCreateInstance(CLSID_SpNotifyTranslator); if (SUCCEEDED(hr)) { hr = cpTranslator->InitWin32Event(NULL, TRUE); } if (SUCCEEDED(hr)) { // // In this case we do NOT call _SetNotify sink since we want to set the cpEventTranslator // m_pParent->Lock(); m_NotifyObjChangeCrit.Lock(); m_cpEventTranslator = cpTranslator; m_cpNotifySink = cpTranslator; if (m_cpNotifySink && m_CompletedList.GetHead()) { m_cpNotifySink->Notify(); } m_NotifyObjChangeCrit.Unlock(); m_pParent->Unlock(); } return hr; } /**************************************************************************** * CSpEventSource::_WaitForNotifyEvent * *-------------------------------------* * Description: * * Returns: * ********************************************************************* RAL ***/ inline HRESULT CSpEventSource::_WaitForNotifyEvent(DWORD dwMilliseconds) { SPDBG_FUNC("CSpEventSource::_WaitForNotifyEvent"); HRESULT hr = S_OK; m_NotifyObjChangeCrit.Lock(); if (m_cpEventTranslator) { hr = m_cpEventTranslator->Wait(dwMilliseconds); } else { if (m_cpNotifySink) { hr = SPERR_ALREADY_INITIALIZED; } else { hr = _SetNotifyWin32Event(); if (SUCCEEDED(hr)) { hr = m_cpEventTranslator->Wait(dwMilliseconds); } } } m_NotifyObjChangeCrit.Unlock(); return hr; } /**************************************************************************** * CSpEventSource::_GetNotifyEventHandle * *---------------------------------------* * Description: * * Returns: * ********************************************************************* RAL ***/ inline HANDLE CSpEventSource::_GetNotifyEventHandle() { HANDLE h = NULL; SPDBG_FUNC("CSpEventSource::_GetNotifyEventHandle"); m_NotifyObjChangeCrit.Lock(); if (!m_cpNotifySink) { _SetNotifyWin32Event(); } if (m_cpEventTranslator) { h = m_cpEventTranslator->GetEventHandle(); } m_NotifyObjChangeCrit.Unlock(); return h; } inline HRESULT CSpEventSource::_SetInterest( ULONGLONG ullEventInterest, ULONGLONG ullQueuedInterest ) { HRESULT hr = S_OK; m_pParent->Lock(); if(ullEventInterest && SPFEI_FLAGCHECK != (ullEventInterest & SPFEI_FLAGCHECK)) { hr = E_INVALIDARG; } else if(ullQueuedInterest && SPFEI_FLAGCHECK != (ullQueuedInterest & SPFEI_FLAGCHECK)) { hr = E_INVALIDARG; } else if ((ullQueuedInterest | ullEventInterest) != ullEventInterest) { hr = E_INVALIDARG; } else { m_ullEventInterest = ullEventInterest; m_ullQueuedInterest = ullQueuedInterest; } m_pParent->Unlock(); return hr; } // // Same as AddEvents except: No param validation, and caller must take the critical section // prior to calling. // inline HRESULT CSpEventSource::_AddEvents( const SPEVENT* pEventArray, ULONG ulCount ) { HRESULT hr = S_OK; for( ULONG i = 0; i < ulCount && SUCCEEDED(hr = _AddEvent(pEventArray[i])); ++i ) {} return hr; } inline HRESULT CSpEventSource::_AddEvent(const SPEVENT & Event) { SPDBG_ASSERT(Event.eEventId < 64); SPDBG_ASSERT(Event.elParamType == SPET_LPARAM_IS_UNDEFINED || Event.elParamType == SPET_LPARAM_IS_TOKEN || Event.elParamType == SPET_LPARAM_IS_OBJECT || Event.elParamType == SPET_LPARAM_IS_POINTER || Event.elParamType == SPET_LPARAM_IS_STRING); #ifdef _DEBUG if (Event.eEventId == SPEI_VOICE_CHANGE) { SPDBG_ASSERT(Event.elParamType == SPET_LPARAM_IS_TOKEN); } else if (Event.eEventId == SPEI_RECOGNITION || Event.eEventId == SPEI_FALSE_RECOGNITION || Event.eEventId == SPEI_HYPOTHESIS) { SPDBG_ASSERT(Event.elParamType == SPET_LPARAM_IS_OBJECT); } else if (Event.eEventId ==SPEI_REQUEST_UI || Event.eEventId == SPEI_TTS_BOOKMARK) { SPDBG_ASSERT(Event.elParamType == SPET_LPARAM_IS_STRING); } #endif if ( (1i64 << Event.eEventId) & m_ullEventInterest ) { CSpEventNode *pNode = m_FreeList.RemoveHead(); if (pNode == NULL) { pNode = new CSpEventNode(); if (pNode == NULL) { return E_OUTOFMEMORY; } } pNode->CopyFrom(&Event); m_PendingList.InsertSorted(pNode); } return S_OK; } inline HRESULT CSpEventSource:: _DeserializeAndAddEvent(const BYTE *pBuffer, ULONG * pcbUsed) { HRESULT hr = S_OK; const SPEVENT * pSrcEvent = (const SPEVENT *)pBuffer; SPDBG_ASSERT(pSrcEvent->eEventId < 64); if ( (1i64 << pSrcEvent->eEventId) & m_ullEventInterest ) { CSpEventNode *pNode = m_FreeList.RemoveHead(); if (pNode == NULL) { pNode = new CSpEventNode(); if (pNode == NULL) { hr = E_OUTOFMEMORY; } } if (SUCCEEDED(hr)) { hr = pNode->Deserialize(((const SPSERIALIZEDEVENT64 *)(pBuffer)), pcbUsed); if (SUCCEEDED(hr)) { m_PendingList.InsertSorted(pNode); } else { m_FreeList.InsertHead(pNode); } } } else { // WCE compiler does not work propertly with template #ifndef _WIN32_WCE *pcbUsed = SpEventSerializeSize(pSrcEvent); #else *pcbUsed = SpEventSerializeSize(pSrcEvent, sizeof(SPSERIALIZEDEVENT64)); #endif } return hr; } inline HRESULT CSpEventSource::_GetEvents( ULONG ulCount, SPEVENT* pEventArray, ULONG *pulFetched ) { HRESULT hr = S_OK; m_pParent->Lock(); if( SPIsBadWritePtr( pEventArray, sizeof( SPEVENT ) * ulCount ) || SP_IS_BAD_OPTIONAL_WRITE_PTR(pulFetched) ) { hr = E_INVALIDARG; } else { ULONG ulCopied = 0; ULONG ulRemaining = ulCount; CSpEventNode * pCur = m_CompletedList.m_pHead; CSpEventNode * pLastCopied = NULL; while (ulRemaining && pCur) { pCur->Detach(pEventArray + ulCopied); pLastCopied = pCur; ulCopied++; pCur = pCur->m_pNext; ulRemaining--; } if (ulCopied) { if (m_FreeList.m_pHead == NULL) { m_FreeList.m_pTail = pLastCopied; } pLastCopied->m_pNext = m_FreeList.m_pHead; m_FreeList.m_pHead = m_CompletedList.m_pHead; m_CompletedList.m_pHead = pCur; m_CompletedList.m_cElements -= ulCopied; m_FreeList.m_cElements += ulCopied; } if (ulCopied < ulCount) { hr = S_FALSE; } if (pulFetched) { *pulFetched = ulCopied; } } m_pParent->Unlock(); return hr; } inline HRESULT CSpEventSource::_GetInfo( SPEVENTSOURCEINFO * pInfo ) { HRESULT hr = S_OK; m_pParent->Lock(); if( SP_IS_BAD_WRITE_PTR( pInfo ) ) { hr = E_POINTER; } else { pInfo->ulCount = m_CompletedList.GetCount(); pInfo->ullEventInterest = m_ullEventInterest; pInfo->ullQueuedInterest= m_ullQueuedInterest; } m_pParent->Unlock(); return hr; } // // The caller must call this function with the critical section owned // inline HRESULT CSpEventSource::_CompleteEvents( ULONGLONG ullPos ) { HRESULT hr = S_OK; if (m_PendingList.m_pHead && m_PendingList.m_pHead->ullAudioStreamOffset <= ullPos) { BOOL bNotify = FALSE; while (m_PendingList.m_pHead && m_PendingList.m_pHead->ullAudioStreamOffset <= ullPos) { CSpEventNode *pNode = m_PendingList.RemoveHead(); if(pNode->ulStreamNum != m_ulStreamNum) { m_ulStreamNum = pNode->ulStreamNum; } if ( (1i64 << pNode->eEventId) & m_ullEventInterest ) { bNotify = TRUE; // // NOTE: If we're forwarding events to an event sink then we'll only // pay attention to the Interest flags. If we're going to notify, then // we'll only queue completed events that the user has explicitly asked // us to store as completed events. // if ( (1i64 << pNode->eEventId) & m_ullQueuedInterest ) { m_CompletedList.InsertSorted(pNode); } else { pNode->Clear(); m_FreeList.InsertHead(pNode); } } else { pNode->Clear(); m_FreeList.InsertHead(pNode); } } if (bNotify && m_cpNotifySink) { hr = m_cpNotifySink->Notify(); } } return hr; }; inline void CSpEventSource::_MoveAllToFreeList(CSpEventList * pList) { CSpEventNode * pNode; while ((pNode = pList->RemoveHead()) != NULL) { pNode->Clear(); m_FreeList.InsertHead(pNode); } } inline void CSpEventSource::_RemoveAllEvents( ) { m_pParent->Lock(); _MoveAllToFreeList(&m_CompletedList); _MoveAllToFreeList(&m_PendingList); m_pParent->Unlock(); } inline HRESULT CSpEventSource::_GetStreamNumber(const ULONGLONG ullAudioOffset, ULONG *pulStreamNum) { CSpEventNode *pNode = m_PendingList.m_pHead; *pulStreamNum = m_ulStreamNum; for(;pNode && pNode->ullAudioStreamOffset <= ullAudioOffset; pNode = pNode->m_pNext) { *pulStreamNum = pNode->ulStreamNum; } return S_OK; } #endif //--- This must be the last line in this file