770 lines
19 KiB
C++
770 lines
19 KiB
C++
//------------------------------------------------------------------------------
|
|
// 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;
|
|
|
|
#ifdef _X86_
|
|
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;
|
|
}
|
|
|
|
|