xpmgr/BuildTools/Include/speventq.h

906 lines
28 KiB
C++

/*******************************************************************************
// 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 <SPHelper.h>
#endif
#ifdef ETW_TRACE_HEADER
#ifdef SOFTWARE_TRACING
#include <speventq.tmh>
#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<const CSpEventExNode *>(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 <class T>
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<ULONG>(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<WPARAM>(pTemp->SerializedwParam);
this->lParam = static_cast<LPARAM>(pTemp->SerializedlParam);
size_t cbAlloc = 0;
if (pTemp->SerializedlParam)
{
switch (pTemp->elParamType)
{
case SPET_LPARAM_IS_POINTER:
cbAlloc = static_cast<ULONG>(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<CSpEventExNode, TRUE, TRUE> 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<CComMultiThreadModel> * 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 <class T>
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<ISpNotifySink> m_cpNotifySink;
CComPtr<ISpNotifyTranslator> m_cpEventTranslator; // If non-NULL then Win32 events being used
CComObjectRootEx<CComMultiThreadModel> * 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<ISpNotifyTranslator> 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<ISpNotifyTranslator> 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<ISpNotifyTranslator> 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<ISpNotifyTranslator> 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