/******************************************************************************* // Copyright Microsoft Corporation. All Rights Reserved. * SPEventQ.h * *------------* * Description: * This is the header file for the SAPI5 event queue implementation. *------------------------------------------------------------------------------- *******************************************************************************/ #ifndef SPEventQ_h #define SPEventQ_h #ifndef SPHelper_h #include #endif #ifdef ETW_TRACE_HEADER #ifdef SOFTWARE_TRACING #include #endif #endif //=== Inline helpers for copying and deleting events ============================ //=== Class definition ========================================================== // This class wraps the SPEVENTEX structure providing a next pointer to allow it to be used in queues; // and helper methods to serialize and deserialize. // This has some similar code to CSpEvent in sphelper.h, but that method is public and not based on // SPEVENTEX. class CSpEventExNode : public SPEVENTEX { public: CSpEventExNode() { memset(this, 0, sizeof(SPEVENTEX)); } ~CSpEventExNode() { Clear(); } CSpEventExNode * m_pNext; static LONG Compare(const CSpEventExNode * p1, const CSpEventExNode *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; } // Clears the event and releases associated memory void Clear() { if (elParamType != SPEI_UNDEFINED) { if (elParamType == SPET_LPARAM_IS_POINTER || elParamType == SPET_LPARAM_IS_STRING) { ::CoTaskMemFree((void *)lParam); } else if (elParamType == SPET_LPARAM_IS_TOKEN || elParamType == SPET_LPARAM_IS_OBJECT) { ((IUnknown*)lParam)->Release(); } } memset(this, 0, sizeof(SPEVENTEX)); } // Copy data from this class to an SPEVENTEX, making new copies of associated memory HRESULT CopyTo(SPEVENTEX * pDestEvent) const { memcpy(pDestEvent, this, sizeof(*pDestEvent)); if ((elParamType == SPET_LPARAM_IS_POINTER) && lParam) { _ASSERT(wParam && (wParam < 0x100000)); // this is too big! pDestEvent->lParam = (LPARAM)::CoTaskMemAlloc(wParam); if (pDestEvent->lParam) { memcpy((void *)pDestEvent->lParam, (void *)lParam, wParam); } else { pDestEvent->eEventId = SPEI_UNDEFINED; return E_OUTOFMEMORY; } } else if (elParamType == SPET_LPARAM_IS_STRING && lParam != NULL) { size_t cbLen = (wcslen((WCHAR*)lParam) + 1) * sizeof(WCHAR); pDestEvent->lParam = (LPARAM)::CoTaskMemAlloc(cbLen); if (pDestEvent->lParam) { StringCbCopyW ((WCHAR*)pDestEvent->lParam, cbLen, (WCHAR*)lParam); } else { pDestEvent->eEventId = SPEI_UNDEFINED; return E_OUTOFMEMORY; } } else if (elParamType == SPET_LPARAM_IS_TOKEN || elParamType == SPET_LPARAM_IS_OBJECT) { ((IUnknown*)lParam)->AddRef(); } return S_OK; } // Initialize this object from an existing SPEVENTEX, copying associated memory HRESULT CopyFrom(const SPEVENTEX * pSrcEvent) { Clear(); return static_cast(pSrcEvent)->CopyTo(this); } // Finds the additional size of data needed to be appended to the end of the event when serializing ULONG ExtraDataSize() const { ULONG ulSize = 0; if( ( elParamType == SPET_LPARAM_IS_POINTER ) && lParam ) { ulSize += ULONG(wParam); } else if ((elParamType == SPET_LPARAM_IS_STRING) && lParam != NULL) { // Would be better to check for overflow of string length. ulSize += ((ULONG) wcslen((WCHAR*)lParam) + 1) * sizeof( WCHAR ); } else if( elParamType == SPET_LPARAM_IS_TOKEN ) { CSpDynamicString dstrObjectId; if( ((ISpObjectToken*)(lParam))->GetId( &dstrObjectId ) == S_OK ) { ulSize += (dstrObjectId.Length() + 1) * sizeof( WCHAR ); } else { return (ULONG)-1; } } // Round up to nearest DWORD ulSize += 3; ulSize -= ulSize % 4; return ulSize; } // Size of SPSERIALIZEDEVENT64 with ullAudioTimeOffset appended ULONG SerializeSizeEx() const { ULONG ulSize = ExtraDataSize(); return (ulSize == (ULONG)-1) ? 0 : sizeof(SPSERIALIZEDEVENT64) + ulSize + sizeof(ULONGLONG); } // Size of regular 32 bit SPSERIALIZEDEVENT ULONG SerializeSize() const { ULONG ulSize = ExtraDataSize(); return (ulSize == (ULONG)-1) ? 0 : sizeof(SPSERIALIZEDEVENT) + ulSize; } // Call this method with either SPSERIALIZEDEVENT or SPSERIALIZEDEVENT64 template HRESULT Serialize(T * pSerEvent) const { HRESULT hr = S_OK; _ASSERT(elParamType != SPET_LPARAM_IS_OBJECT); pSerEvent->eEventId = this->eEventId; pSerEvent->elParamType = this->elParamType; pSerEvent->ulStreamNum = this->ulStreamNum; pSerEvent->ullAudioStreamOffset = this->ullAudioStreamOffset; SerializeParams(pSerEvent); if (lParam) { switch(elParamType) { case SPET_LPARAM_IS_POINTER: memcpy(pSerEvent + 1, (void *)lParam, wParam); pSerEvent->SerializedlParam = sizeof(T); break; case SPET_LPARAM_IS_STRING: StringCchCopyW ((WCHAR *)(pSerEvent + 1), wcslen ((WCHAR*) lParam) + 1, (WCHAR*)lParam); pSerEvent->SerializedlParam = sizeof(T); break; case SPET_LPARAM_IS_TOKEN: { CSpDynamicString dstrObjectId; hr = ((ISpObjectToken*)lParam)->GetId( &dstrObjectId ); if( SUCCEEDED( hr ) ) { pSerEvent->SerializedwParam = (dstrObjectId.Length() + 1) * sizeof( WCHAR );; memcpy( pSerEvent + 1, (void *)dstrObjectId.m_psz, static_cast(pSerEvent->SerializedwParam) ); } pSerEvent->SerializedlParam = sizeof(T); } break; default: break; } } return hr; } void SerializeParams(__unaligned SPSERIALIZEDEVENT * pSerEvent) const { pSerEvent->SerializedwParam = (ULONG)this->wParam; pSerEvent->SerializedlParam = (LONG)this->lParam; } void SerializeParams(__unaligned SPSERIALIZEDEVENT64 * pSerEvent) const { pSerEvent->SerializedwParam = (ULONGLONG)this->wParam; pSerEvent->SerializedlParam = (LONGLONG)this->lParam; } // Serialize into an SPSERIALIZEDEVENT64 structure. The ullAudioTimeOffset field is appended at // the end of the other serialized data. This is to make this structure binary compatible with SAPI 5.1 // code trying to deserialize it. HRESULT SerializeEx(SPSERIALIZEDEVENT64* pSerEvent) const { ULONG ulSize = SerializeSizeEx(); if (ulSize == 0) { return E_FAIL; } *(UNALIGNED ULONGLONG*)((BYTE*)pSerEvent + ulSize - sizeof(ULONGLONG)) = ullAudioTimeOffset; return Serialize(pSerEvent); } // Deserialize into this class, assuming ullAudioTimeOffset included HRESULT DeserializeEx(const SPSERIALIZEDEVENT64 *pSerEvent, ULONG cbSize) { Clear(); HRESULT hr = S_OK; const UNALIGNED SPSERIALIZEDEVENT64 * pTemp = pSerEvent; if (cbSize < sizeof(*pTemp) + sizeof(ULONGLONG)) { return E_INVALIDARG; } size_t cbExtraSize = cbSize - sizeof(*pTemp) - sizeof(ULONGLONG); this->eEventId = pTemp->eEventId; this->elParamType = pTemp->elParamType; this->ulStreamNum = pTemp->ulStreamNum; this->ullAudioStreamOffset = pTemp->ullAudioStreamOffset; this->wParam = static_cast(pTemp->SerializedwParam); this->lParam = static_cast(pTemp->SerializedlParam); size_t cbAlloc = 0; if (pTemp->SerializedlParam) { switch (pTemp->elParamType) { case SPET_LPARAM_IS_POINTER: cbAlloc = static_cast(wParam); if (cbAlloc > cbExtraSize) { hr = E_INVALIDARG; } break; case SPET_LPARAM_IS_STRING: hr = CheckStringSizeBytes((WCHAR *)(pTemp + 1), cbExtraSize, &cbAlloc); break; case SPET_LPARAM_IS_TOKEN: { hr = CheckStringSizeBytes((WCHAR *)(pTemp + 1), cbExtraSize, NULL); if (SUCCEEDED(hr)) { hr = SpGetTokenFromId( (const WCHAR*)(pTemp + 1), (ISpObjectToken **)&lParam ); wParam = 0; } } break; case SPET_LPARAM_IS_UNDEFINED: break; case SPET_LPARAM_IS_OBJECT: hr = E_INVALIDARG; break; default: hr = E_INVALIDARG; break; } if (SUCCEEDED(hr) && cbAlloc) { void * pvBuff = ::CoTaskMemAlloc(cbAlloc); this->lParam = (LPARAM)pvBuff; if (pvBuff) { memcpy(pvBuff, pTemp + 1, cbAlloc); } else { hr = E_OUTOFMEMORY; } } } if (SUCCEEDED(hr)) { ullAudioTimeOffset = *(UNALIGNED ULONGLONG*)((BYTE*)pTemp + cbSize - sizeof(ULONGLONG)); _ASSERT(cbSize == SerializeSizeEx()); } else { memset(this, 0, sizeof(*this)); } return hr; } }; 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, bool fSortEvents = true) : m_pParent(pParent), m_fSortEvents(fSortEvents) { 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); template inline HRESULT _GetEvents( ULONG ulCount, T* pEventArray, ULONG *pulFetched ) { HRESULT hr = S_OK; m_pParent->Lock(); if( SPIsBadWritePtr( pEventArray, sizeof(T) * ulCount ) || SP_IS_BAD_OPTIONAL_WRITE_PTR(pulFetched) ) { hr = E_INVALIDARG; } else { ULONG ulCopied = 0; ULONG ulRemaining = ulCount; CSpEventExNode * pCur = m_CompletedList.m_pHead; CSpEventExNode * pLastCopied = NULL; while (ulRemaining && pCur) { #ifdef ETW_TRACE_HEADER #ifdef SOFTWARE_TRACING if(pCur->eEventId == SPEI_RECOGNITION || pCur->eEventId == SPEI_FALSE_RECOGNITION || pCur->eEventId == SPEI_RECOGNITION_ALL) { DoTraceMessage(Trace_Verbose, "CpEventSource::Removed event with offset %I64u and Id %u from queue",pCur->ullAudioStreamOffset,pCur->eEventId); } #endif #endif // Detach the event into the passed in array and clear our copy memcpy(pEventArray + ulCopied, pCur, sizeof(T)); memset((SPEVENTEX*)pCur, 0, sizeof(SPEVENTEX)); 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; } HRESULT _GetInfo(SPEVENTSOURCEINFO *pInfo ); /*--- Non interface methods ---*/ HRESULT _CompleteEvents( ULONGLONG ullPos = 0xFFFFFFFFFFFFFFFF ); inline void _MoveAllToFreeList(CSpEventList * pList); inline void _RemoveAllEvents(); inline HRESULT _AddEvent(const SPEVENTEX & Event); inline HRESULT _AddEvents(const SPEVENT* pEventArray, ULONG ulCount); inline HRESULT _AddEvents(const SPEVENTEX* pEventArray, ULONG ulCount); 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. private: bool m_fSortEvents; }; // //=== 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: * *****************************************************************************/ inline HRESULT CSpEventSource::_SetNotifyWindowMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { 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: * *****************************************************************************/ inline HRESULT CSpEventSource::_SetNotifyCallbackFunction(SPNOTIFYCALLBACK * pfnCallback, WPARAM wParam, LPARAM lParam) { 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: * *****************************************************************************/ inline HRESULT CSpEventSource::_SetNotifyCallbackInterface(ISpNotifyCallback * pSpCallback, WPARAM wParam, LPARAM lParam) { 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: * *****************************************************************************/ inline HRESULT CSpEventSource::_SetNotifyWin32Event(void) { 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: * *****************************************************************************/ inline HRESULT CSpEventSource::_WaitForNotifyEvent(DWORD dwMilliseconds) { 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: * *****************************************************************************/ inline HANDLE CSpEventSource::_GetNotifyEventHandle() { HANDLE h = NULL; 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); ++i ) { SPEVENTEX EventEx; memcpy(&EventEx, pEventArray + i, sizeof(SPEVENT)); EventEx.ullAudioTimeOffset = 0; // We set the audio time to zero if no information available hr = _AddEvent(EventEx); } return hr; } inline HRESULT CSpEventSource::_AddEvents( const SPEVENTEX* 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 SPEVENTEX & Event) { _ASSERT(Event.eEventId < 64); _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) { _ASSERT(Event.elParamType == SPET_LPARAM_IS_TOKEN); } else if (Event.eEventId == SPEI_RECOGNITION || Event.eEventId == SPEI_FALSE_RECOGNITION || Event.eEventId == SPEI_HYPOTHESIS) { _ASSERT(Event.elParamType == SPET_LPARAM_IS_OBJECT); } else if (Event.eEventId ==SPEI_REQUEST_UI || Event.eEventId == SPEI_TTS_BOOKMARK) { _ASSERT(Event.elParamType == SPET_LPARAM_IS_STRING); } #endif if ( (1i64 << Event.eEventId) & m_ullEventInterest ) { CSpEventExNode *pNode = m_FreeList.RemoveHead(); if (pNode == NULL) { pNode = new CSpEventExNode(); if (pNode == NULL) { return E_OUTOFMEMORY; } } pNode->CopyFrom(&Event); if (m_fSortEvents) { m_PendingList.InsertSorted(pNode); } else { m_PendingList.InsertTail(pNode); } } return S_OK; } 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) { CSpEventExNode *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 ) { if (m_fSortEvents) { m_CompletedList.InsertSorted(pNode); } else { m_CompletedList.InsertTail(pNode); } #ifdef ETW_TRACE_HEADER #ifdef SOFTWARE_TRACING if(pNode->eEventId == SPEI_RECOGNITION || pNode->eEventId == SPEI_FALSE_RECOGNITION || pNode->eEventId == SPEI_RECOGNITION_ALL) { DoTraceMessage(Trace_Verbose, "CpEventSource::Added reco event with offset %I64u and event id %u to queue",pNode->ullAudioStreamOffset,pNode->eEventId); } #endif #endif } 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) { CSpEventExNode * 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) { CSpEventExNode *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