diff options
author | Casimir666 <casimir666@users.sourceforge.net> | 2009-03-01 12:49:27 +0300 |
---|---|---|
committer | Casimir666 <casimir666@users.sourceforge.net> | 2009-03-01 12:49:27 +0300 |
commit | 7a7ddcb79fd2de74e1105cd7aebb8acfbd76cb6b (patch) | |
tree | 000c2216c29c6d52701666b4b1388a2a841025eb /src/filters/BaseClasses/wxutil.cpp | |
parent | 3f89e9541a0031cf320bb412855ce9131ef0e4a1 (diff) |
Added: Microsoft BaseClasses to the SVN
git-svn-id: https://mpc-hc.svn.sourceforge.net/svnroot/mpc-hc/trunk@1006 10f7b99b-c216-0410-bff0-8a66a9350fd8
Diffstat (limited to 'src/filters/BaseClasses/wxutil.cpp')
-rw-r--r-- | src/filters/BaseClasses/wxutil.cpp | 769 |
1 files changed, 769 insertions, 0 deletions
diff --git a/src/filters/BaseClasses/wxutil.cpp b/src/filters/BaseClasses/wxutil.cpp new file mode 100644 index 000000000..5bc97a9ae --- /dev/null +++ b/src/filters/BaseClasses/wxutil.cpp @@ -0,0 +1,769 @@ +//------------------------------------------------------------------------------
+// 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;
+}
+
+
|