mirror of https://github.com/UMSKT/xpmgr.git
906 lines
28 KiB
C++
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
|
|
|