//------------------------------------------------------------------------------ | |
// File: WXUtil.cpp | |
// | |
// Desc: DirectShow base classes - implements helper classes for building | |
// multimedia filters. | |
// | |
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. | |
//------------------------------------------------------------------------------ | |
#include <streams.h> | |
#define STRSAFE_NO_DEPRECATE | |
#include <strsafe.h> | |
// --- CAMEvent ----------------------- | |
CAMEvent::CAMEvent(BOOL fManualReset, __inout_opt HRESULT *phr) | |
{ | |
m_hEvent = CreateEvent(NULL, fManualReset, FALSE, NULL); | |
if (NULL == m_hEvent) { | |
if (NULL != phr && SUCCEEDED(*phr)) { | |
*phr = E_OUTOFMEMORY; | |
} | |
} | |
} | |
CAMEvent::CAMEvent(__inout_opt HRESULT *phr) | |
{ | |
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); | |
if (NULL == m_hEvent) { | |
if (NULL != phr && SUCCEEDED(*phr)) { | |
*phr = E_OUTOFMEMORY; | |
} | |
} | |
} | |
CAMEvent::~CAMEvent() | |
{ | |
if (m_hEvent) { | |
EXECUTE_ASSERT(CloseHandle(m_hEvent)); | |
} | |
} | |
// --- CAMMsgEvent ----------------------- | |
// One routine. The rest is handled in CAMEvent | |
CAMMsgEvent::CAMMsgEvent(__inout_opt HRESULT *phr) : CAMEvent(FALSE, phr) | |
{ | |
} | |
BOOL CAMMsgEvent::WaitMsg(DWORD dwTimeout) | |
{ | |
// wait for the event to be signalled, or for the | |
// timeout (in MS) to expire. allow SENT messages | |
// to be processed while we wait | |
DWORD dwWait; | |
DWORD dwStartTime; | |
// set the waiting period. | |
DWORD dwWaitTime = dwTimeout; | |
// the timeout will eventually run down as we iterate | |
// processing messages. grab the start time so that | |
// we can calculate elapsed times. | |
if (dwWaitTime != INFINITE) { | |
dwStartTime = timeGetTime(); | |
} | |
do { | |
dwWait = MsgWaitForMultipleObjects(1,&m_hEvent,FALSE, dwWaitTime, QS_SENDMESSAGE); | |
if (dwWait == WAIT_OBJECT_0 + 1) { | |
MSG Message; | |
PeekMessage(&Message,NULL,0,0,PM_NOREMOVE); | |
// If we have an explicit length of time to wait calculate | |
// the next wake up point - which might be now. | |
// If dwTimeout is INFINITE, it stays INFINITE | |
if (dwWaitTime != INFINITE) { | |
DWORD dwElapsed = timeGetTime()-dwStartTime; | |
dwWaitTime = | |
(dwElapsed >= dwTimeout) | |
? 0 // wake up with WAIT_TIMEOUT | |
: dwTimeout-dwElapsed; | |
} | |
} | |
} while (dwWait == WAIT_OBJECT_0 + 1); | |
// return TRUE if we woke on the event handle, | |
// FALSE if we timed out. | |
return (dwWait == WAIT_OBJECT_0); | |
} | |
// --- CAMThread ---------------------- | |
CAMThread::CAMThread(__inout_opt HRESULT *phr) | |
: m_EventSend(TRUE, phr), // must be manual-reset for CheckRequest() | |
m_EventComplete(FALSE, phr) | |
{ | |
m_hThread = NULL; | |
} | |
CAMThread::~CAMThread() { | |
Close(); | |
} | |
// when the thread starts, it calls this function. We unwrap the 'this' | |
//pointer and call ThreadProc. | |
DWORD WINAPI | |
CAMThread::InitialThreadProc(__inout LPVOID pv) | |
{ | |
HRESULT hrCoInit = CAMThread::CoInitializeHelper(); | |
if(FAILED(hrCoInit)) { | |
DbgLog((LOG_ERROR, 1, TEXT("CoInitializeEx failed."))); | |
} | |
CAMThread * pThread = (CAMThread *) pv; | |
HRESULT hr = pThread->ThreadProc(); | |
if(SUCCEEDED(hrCoInit)) { | |
CoUninitialize(); | |
} | |
return hr; | |
} | |
BOOL | |
CAMThread::Create() | |
{ | |
DWORD threadid; | |
CAutoLock lock(&m_AccessLock); | |
if (ThreadExists()) { | |
return FALSE; | |
} | |
m_hThread = CreateThread( | |
NULL, | |
0, | |
CAMThread::InitialThreadProc, | |
this, | |
0, | |
&threadid); | |
if (!m_hThread) { | |
return FALSE; | |
} | |
return TRUE; | |
} | |
DWORD | |
CAMThread::CallWorker(DWORD dwParam) | |
{ | |
// lock access to the worker thread for scope of this object | |
CAutoLock lock(&m_AccessLock); | |
if (!ThreadExists()) { | |
return (DWORD) E_FAIL; | |
} | |
// set the parameter | |
m_dwParam = dwParam; | |
// signal the worker thread | |
m_EventSend.Set(); | |
// wait for the completion to be signalled | |
m_EventComplete.Wait(); | |
// done - this is the thread's return value | |
return m_dwReturnVal; | |
} | |
// Wait for a request from the client | |
DWORD | |
CAMThread::GetRequest() | |
{ | |
m_EventSend.Wait(); | |
return m_dwParam; | |
} | |
// is there a request? | |
BOOL | |
CAMThread::CheckRequest(__out_opt DWORD * pParam) | |
{ | |
if (!m_EventSend.Check()) { | |
return FALSE; | |
} else { | |
if (pParam) { | |
*pParam = m_dwParam; | |
} | |
return TRUE; | |
} | |
} | |
// reply to the request | |
void | |
CAMThread::Reply(DWORD dw) | |
{ | |
m_dwReturnVal = dw; | |
// The request is now complete so CheckRequest should fail from | |
// now on | |
// | |
// This event should be reset BEFORE we signal the client or | |
// the client may Set it before we reset it and we'll then | |
// reset it (!) | |
m_EventSend.Reset(); | |
// Tell the client we're finished | |
m_EventComplete.Set(); | |
} | |
HRESULT CAMThread::CoInitializeHelper() | |
{ | |
// call CoInitializeEx and tell OLE not to create a window (this | |
// thread probably won't dispatch messages and will hang on | |
// broadcast msgs o/w). | |
// | |
// If CoInitEx is not available, threads that don't call CoCreate | |
// aren't affected. Threads that do will have to handle the | |
// failure. Perhaps we should fall back to CoInitialize and risk | |
// hanging? | |
// | |
// older versions of ole32.dll don't have CoInitializeEx | |
HRESULT hr = E_FAIL; | |
HINSTANCE hOle = GetModuleHandle(TEXT("ole32.dll")); | |
if(hOle) | |
{ | |
typedef HRESULT (STDAPICALLTYPE *PCoInitializeEx)( | |
LPVOID pvReserved, DWORD dwCoInit); | |
PCoInitializeEx pCoInitializeEx = | |
(PCoInitializeEx)(GetProcAddress(hOle, "CoInitializeEx")); | |
if(pCoInitializeEx) | |
{ | |
hr = (*pCoInitializeEx)(0, COINIT_DISABLE_OLE1DDE ); | |
} | |
} | |
else | |
{ | |
// caller must load ole32.dll | |
DbgBreak("couldn't locate ole32.dll"); | |
} | |
return hr; | |
} | |
// destructor for CMsgThread - cleans up any messages left in the | |
// queue when the thread exited | |
CMsgThread::~CMsgThread() | |
{ | |
if (m_hThread != NULL) { | |
WaitForSingleObject(m_hThread, INFINITE); | |
EXECUTE_ASSERT(CloseHandle(m_hThread)); | |
} | |
POSITION pos = m_ThreadQueue.GetHeadPosition(); | |
while (pos) { | |
CMsg * pMsg = m_ThreadQueue.GetNext(pos); | |
delete pMsg; | |
} | |
m_ThreadQueue.RemoveAll(); | |
if (m_hSem != NULL) { | |
EXECUTE_ASSERT(CloseHandle(m_hSem)); | |
} | |
} | |
BOOL | |
CMsgThread::CreateThread( | |
) | |
{ | |
m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL); | |
if (m_hSem == NULL) { | |
return FALSE; | |
} | |
m_hThread = ::CreateThread(NULL, 0, DefaultThreadProc, | |
(LPVOID)this, 0, &m_ThreadId); | |
return m_hThread != NULL; | |
} | |
// This is the threads message pump. Here we get and dispatch messages to | |
// clients thread proc until the client refuses to process a message. | |
// The client returns a non-zero value to stop the message pump, this | |
// value becomes the threads exit code. | |
DWORD WINAPI | |
CMsgThread::DefaultThreadProc( | |
__inout LPVOID lpParam | |
) | |
{ | |
CMsgThread *lpThis = (CMsgThread *)lpParam; | |
CMsg msg; | |
LRESULT lResult; | |
// !!! | |
CoInitialize(NULL); | |
// allow a derived class to handle thread startup | |
lpThis->OnThreadInit(); | |
do { | |
lpThis->GetThreadMsg(&msg); | |
lResult = lpThis->ThreadMessageProc(msg.uMsg,msg.dwFlags, | |
msg.lpParam, msg.pEvent); | |
} while (lResult == 0L); | |
// !!! | |
CoUninitialize(); | |
return (DWORD)lResult; | |
} | |
// Block until the next message is placed on the list m_ThreadQueue. | |
// copies the message to the message pointed to by *pmsg | |
void | |
CMsgThread::GetThreadMsg(__out CMsg *msg) | |
{ | |
CMsg * pmsg = NULL; | |
// keep trying until a message appears | |
while (TRUE) { | |
{ | |
CAutoLock lck(&m_Lock); | |
pmsg = m_ThreadQueue.RemoveHead(); | |
if (pmsg == NULL) { | |
m_lWaiting++; | |
} else { | |
break; | |
} | |
} | |
// the semaphore will be signalled when it is non-empty | |
WaitForSingleObject(m_hSem, INFINITE); | |
} | |
// copy fields to caller's CMsg | |
*msg = *pmsg; | |
// this CMsg was allocated by the 'new' in PutThreadMsg | |
delete pmsg; | |
} | |
// Helper function - convert int to WSTR | |
void WINAPI IntToWstr(int i, __out_ecount(12) LPWSTR wstr) | |
{ | |
#ifdef UNICODE | |
if (FAILED(StringCchPrintf(wstr, 12, L"%d", i))) { | |
wstr[0] = 0; | |
} | |
#else | |
TCHAR temp[12]; | |
if (FAILED(StringCchPrintf(temp, NUMELMS(temp), "%d", i))) { | |
wstr[0] = 0; | |
} else { | |
MultiByteToWideChar(CP_ACP, 0, temp, -1, wstr, 12); | |
} | |
#endif | |
} // IntToWstr | |
#define MEMORY_ALIGNMENT 4 | |
#define MEMORY_ALIGNMENT_LOG2 2 | |
#define MEMORY_ALIGNMENT_MASK MEMORY_ALIGNMENT - 1 | |
void * __stdcall memmoveInternal(void * dst, const void * src, size_t count) | |
{ | |
void * ret = dst; | |
#if defined(_X86_) && !defined(__clang__) | |
if (dst <= src || (char *)dst >= ((char *)src + count)) { | |
/* | |
* Non-Overlapping Buffers | |
* copy from lower addresses to higher addresses | |
*/ | |
_asm { | |
mov esi,src | |
mov edi,dst | |
mov ecx,count | |
cld | |
mov edx,ecx | |
and edx,MEMORY_ALIGNMENT_MASK | |
shr ecx,MEMORY_ALIGNMENT_LOG2 | |
rep movsd | |
or ecx,edx | |
jz memmove_done | |
rep movsb | |
memmove_done: | |
} | |
} | |
else { | |
/* | |
* Overlapping Buffers | |
* copy from higher addresses to lower addresses | |
*/ | |
_asm { | |
mov esi,src | |
mov edi,dst | |
mov ecx,count | |
std | |
add esi,ecx | |
add edi,ecx | |
dec esi | |
dec edi | |
rep movsb | |
cld | |
} | |
} | |
#else | |
MoveMemory(dst, src, count); | |
#endif | |
return ret; | |
} | |
HRESULT AMSafeMemMoveOffset( | |
__in_bcount(dst_size) void * dst, | |
__in size_t dst_size, | |
__in DWORD cb_dst_offset, | |
__in_bcount(src_size) const void * src, | |
__in size_t src_size, | |
__in DWORD cb_src_offset, | |
__in size_t count) | |
{ | |
// prevent read overruns | |
if( count + cb_src_offset < count || // prevent integer overflow | |
count + cb_src_offset > src_size) // prevent read overrun | |
{ | |
return E_INVALIDARG; | |
} | |
// prevent write overruns | |
if( count + cb_dst_offset < count || // prevent integer overflow | |
count + cb_dst_offset > dst_size) // prevent write overrun | |
{ | |
return E_INVALIDARG; | |
} | |
memmoveInternal( (BYTE *)dst+cb_dst_offset, (BYTE *)src+cb_src_offset, count); | |
return S_OK; | |
} | |
#ifdef DEBUG | |
/******************************Public*Routine******************************\ | |
* Debug CCritSec helpers | |
* | |
* We provide debug versions of the Constructor, destructor, Lock and Unlock | |
* routines. The debug code tracks who owns each critical section by | |
* maintaining a depth count. | |
* | |
* History: | |
* | |
\**************************************************************************/ | |
CCritSec::CCritSec() | |
{ | |
InitializeCriticalSection(&m_CritSec); | |
m_currentOwner = m_lockCount = 0; | |
m_fTrace = FALSE; | |
} | |
CCritSec::~CCritSec() | |
{ | |
DeleteCriticalSection(&m_CritSec); | |
} | |
void CCritSec::Lock() | |
{ | |
UINT tracelevel=3; | |
DWORD us = GetCurrentThreadId(); | |
DWORD currentOwner = m_currentOwner; | |
if (currentOwner && (currentOwner != us)) { | |
// already owned, but not by us | |
if (m_fTrace) { | |
DbgLog((LOG_LOCKING, 2, TEXT("Thread %d about to wait for lock %x owned by %d"), | |
GetCurrentThreadId(), &m_CritSec, currentOwner)); | |
tracelevel=2; | |
// if we saw the message about waiting for the critical | |
// section we ensure we see the message when we get the | |
// critical section | |
} | |
} | |
EnterCriticalSection(&m_CritSec); | |
if (0 == m_lockCount++) { | |
// we now own it for the first time. Set owner information | |
m_currentOwner = us; | |
if (m_fTrace) { | |
DbgLog((LOG_LOCKING, tracelevel, TEXT("Thread %d now owns lock %x"), m_currentOwner, &m_CritSec)); | |
} | |
} | |
} | |
void CCritSec::Unlock() { | |
if (0 == --m_lockCount) { | |
// about to be unowned | |
if (m_fTrace) { | |
DbgLog((LOG_LOCKING, 3, TEXT("Thread %d releasing lock %x"), m_currentOwner, &m_CritSec)); | |
} | |
m_currentOwner = 0; | |
} | |
LeaveCriticalSection(&m_CritSec); | |
} | |
void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace) | |
{ | |
pcCrit->m_fTrace = fTrace; | |
} | |
BOOL WINAPI CritCheckIn(CCritSec * pcCrit) | |
{ | |
return (GetCurrentThreadId() == pcCrit->m_currentOwner); | |
} | |
BOOL WINAPI CritCheckIn(const CCritSec * pcCrit) | |
{ | |
return (GetCurrentThreadId() == pcCrit->m_currentOwner); | |
} | |
BOOL WINAPI CritCheckOut(CCritSec * pcCrit) | |
{ | |
return (GetCurrentThreadId() != pcCrit->m_currentOwner); | |
} | |
BOOL WINAPI CritCheckOut(const CCritSec * pcCrit) | |
{ | |
return (GetCurrentThreadId() != pcCrit->m_currentOwner); | |
} | |
#endif | |
STDAPI WriteBSTR(__deref_out BSTR *pstrDest, LPCWSTR szSrc) | |
{ | |
*pstrDest = SysAllocString( szSrc ); | |
if( !(*pstrDest) ) return E_OUTOFMEMORY; | |
return NOERROR; | |
} | |
STDAPI FreeBSTR(__deref_in BSTR* pstr) | |
{ | |
if( (PVOID)*pstr == NULL ) return S_FALSE; | |
SysFreeString( *pstr ); | |
return NOERROR; | |
} | |
// Return a wide string - allocating memory for it | |
// Returns: | |
// S_OK - no error | |
// E_POINTER - ppszReturn == NULL | |
// E_OUTOFMEMORY - can't allocate memory for returned string | |
STDAPI AMGetWideString(LPCWSTR psz, __deref_out LPWSTR *ppszReturn) | |
{ | |
CheckPointer(ppszReturn, E_POINTER); | |
ValidateReadWritePtr(ppszReturn, sizeof(LPWSTR)); | |
*ppszReturn = NULL; | |
size_t nameLen; | |
HRESULT hr = StringCbLengthW(psz, 100000, &nameLen); | |
if (FAILED(hr)) { | |
return hr; | |
} | |
*ppszReturn = (LPWSTR)CoTaskMemAlloc(nameLen + sizeof(WCHAR)); | |
if (*ppszReturn == NULL) { | |
return E_OUTOFMEMORY; | |
} | |
CopyMemory(*ppszReturn, psz, nameLen + sizeof(WCHAR)); | |
return NOERROR; | |
} | |
// Waits for the HANDLE hObject. While waiting messages sent | |
// to windows on our thread by SendMessage will be processed. | |
// Using this function to do waits and mutual exclusion | |
// avoids some deadlocks in objects with windows. | |
// Return codes are the same as for WaitForSingleObject | |
DWORD WINAPI WaitDispatchingMessages( | |
HANDLE hObject, | |
DWORD dwWait, | |
HWND hwnd, | |
UINT uMsg, | |
HANDLE hEvent) | |
{ | |
BOOL bPeeked = FALSE; | |
DWORD dwResult; | |
DWORD dwStart; | |
DWORD dwThreadPriority; | |
static UINT uMsgId = 0; | |
HANDLE hObjects[2] = { hObject, hEvent }; | |
if (dwWait != INFINITE && dwWait != 0) { | |
dwStart = GetTickCount(); | |
} | |
for (; ; ) { | |
DWORD nCount = NULL != hEvent ? 2 : 1; | |
// Minimize the chance of actually dispatching any messages | |
// by seeing if we can lock immediately. | |
dwResult = WaitForMultipleObjects(nCount, hObjects, FALSE, 0); | |
if (dwResult < WAIT_OBJECT_0 + nCount) { | |
break; | |
} | |
DWORD dwTimeOut = dwWait; | |
if (dwTimeOut > 10) { | |
dwTimeOut = 10; | |
} | |
dwResult = MsgWaitForMultipleObjects( | |
nCount, | |
hObjects, | |
FALSE, | |
dwTimeOut, | |
hwnd == NULL ? QS_SENDMESSAGE : | |
QS_SENDMESSAGE + QS_POSTMESSAGE); | |
if (dwResult == WAIT_OBJECT_0 + nCount || | |
dwResult == WAIT_TIMEOUT && dwTimeOut != dwWait) { | |
MSG msg; | |
if (hwnd != NULL) { | |
while (PeekMessage(&msg, hwnd, uMsg, uMsg, PM_REMOVE)) { | |
DispatchMessage(&msg); | |
} | |
} | |
// Do this anyway - the previous peek doesn't flush out the | |
// messages | |
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); | |
if (dwWait != INFINITE && dwWait != 0) { | |
DWORD dwNow = GetTickCount(); | |
// Working with differences handles wrap-around | |
DWORD dwDiff = dwNow - dwStart; | |
if (dwDiff > dwWait) { | |
dwWait = 0; | |
} else { | |
dwWait -= dwDiff; | |
} | |
dwStart = dwNow; | |
} | |
if (!bPeeked) { | |
// Raise our priority to prevent our message queue | |
// building up | |
dwThreadPriority = GetThreadPriority(GetCurrentThread()); | |
if (dwThreadPriority < THREAD_PRIORITY_HIGHEST) { | |
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); | |
} | |
bPeeked = TRUE; | |
} | |
} else { | |
break; | |
} | |
} | |
if (bPeeked) { | |
SetThreadPriority(GetCurrentThread(), dwThreadPriority); | |
if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) & QS_POSTMESSAGE) { | |
if (uMsgId == 0) { | |
uMsgId = RegisterWindowMessage(TEXT("AMUnblock")); | |
} | |
if (uMsgId != 0) { | |
MSG msg; | |
// Remove old ones | |
while (PeekMessage(&msg, (HWND)-1, uMsgId, uMsgId, PM_REMOVE)) { | |
} | |
} | |
PostThreadMessage(GetCurrentThreadId(), uMsgId, 0, 0); | |
} | |
} | |
return dwResult; | |
} | |
HRESULT AmGetLastErrorToHResult() | |
{ | |
DWORD dwLastError = GetLastError(); | |
if(dwLastError != 0) | |
{ | |
return HRESULT_FROM_WIN32(dwLastError); | |
} | |
else | |
{ | |
return E_FAIL; | |
} | |
} | |
IUnknown* QzAtlComPtrAssign(__deref_inout_opt IUnknown** pp, __in_opt IUnknown* lp) | |
{ | |
if (lp != NULL) | |
lp->AddRef(); | |
if (*pp) | |
(*pp)->Release(); | |
*pp = lp; | |
return lp; | |
} | |
/****************************************************************************** | |
CompatibleTimeSetEvent | |
CompatibleTimeSetEvent() sets the TIME_KILL_SYNCHRONOUS flag before calling | |
timeSetEvent() if the current operating system supports it. TIME_KILL_SYNCHRONOUS | |
is supported on Windows XP and later operating systems. | |
Parameters: | |
- The same parameters as timeSetEvent(). See timeSetEvent()'s documentation in | |
the Platform SDK for more information. | |
Return Value: | |
- The same return value as timeSetEvent(). See timeSetEvent()'s documentation in | |
the Platform SDK for more information. | |
******************************************************************************/ | |
MMRESULT CompatibleTimeSetEvent( UINT uDelay, UINT uResolution, __in LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent ) | |
{ | |
#if WINVER >= 0x0501 | |
{ | |
static bool fCheckedVersion = false; | |
static bool fTimeKillSynchronousFlagAvailable = false; | |
if( !fCheckedVersion ) { | |
fTimeKillSynchronousFlagAvailable = TimeKillSynchronousFlagAvailable(); | |
fCheckedVersion = true; | |
} | |
if( fTimeKillSynchronousFlagAvailable ) { | |
fuEvent = fuEvent | TIME_KILL_SYNCHRONOUS; | |
} | |
} | |
#endif // WINVER >= 0x0501 | |
return timeSetEvent( uDelay, uResolution, lpTimeProc, dwUser, fuEvent ); | |
} | |
bool TimeKillSynchronousFlagAvailable( void ) | |
{ | |
OSVERSIONINFO osverinfo; | |
osverinfo.dwOSVersionInfoSize = sizeof(osverinfo); | |
if( GetVersionEx( &osverinfo ) ) { | |
// Windows XP's major version is 5 and its' minor version is 1. | |
// timeSetEvent() started supporting the TIME_KILL_SYNCHRONOUS flag | |
// in Windows XP. | |
if( (osverinfo.dwMajorVersion > 5) || | |
( (osverinfo.dwMajorVersion == 5) && (osverinfo.dwMinorVersion >= 1) ) ) { | |
return true; | |
} | |
} | |
return false; | |
} | |