// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1995 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include <process.h>    // for _beginthreadex and _endthreadex
#include <ddeml.h>  // for MSGF_DDEMGR

#ifdef AFX_CORE1_SEG
#pragma code_seg(AFX_CORE1_SEG)
#endif

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// Thread entry point

#ifdef _MT

struct _AFX_THREAD_STARTUP
{
	// following are "in" parameters to thread startup
	_AFX_THREAD_STATE* pThreadState;    // thread state of parent thread
	CWinThread* pThread;    // CWinThread for new thread
	DWORD dwCreateFlags;    // thread creation flags
	_PNH pfnNewHandler;     // new handler for new thread

	HANDLE hEvent;          // event triggered after success/non-success
	HANDLE hEvent2;         // event triggered after thread is resumed

	// strictly "out" -- set after hEvent is triggered
	BOOL bError;    // TRUE if error during startup
};

UINT APIENTRY _AfxThreadEntry(void* pParam)
{
	_AFX_THREAD_STARTUP* pStartup = (_AFX_THREAD_STARTUP*)pParam;
	ASSERT(pStartup != NULL);
	ASSERT(pStartup->pThreadState != NULL);
	ASSERT(pStartup->pThread != NULL);
	ASSERT(pStartup->hEvent != NULL);
	ASSERT(!pStartup->bError);

	CWinThread* pThread = pStartup->pThread;
	CWnd threadWnd;
	TRY
	{
		// inherit parent's module state
		_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
		pThreadState->m_pModuleState = pStartup->pThreadState->m_pModuleState;

		// set current thread pointer for AfxGetThread
		AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
#ifdef _AFXDLL
		pThread->m_pModuleState = pModuleState;
#endif
		AFX_MODULE_THREAD_STATE* pState = pModuleState->m_thread;
		pState->m_pCurrentWinThread = pThread;
		pState->m_pfnNewHandler = pStartup->pfnNewHandler;

		// forced initialization of the thread
		AfxInitThread();

		// thread inherits app's main window if not already set
		CWinApp* pApp = AfxGetApp();
		ASSERT(pApp != NULL);
		if (pThread->m_pMainWnd == NULL && pApp->m_pMainWnd->GetSafeHwnd() != NULL)
		{
			// just attach the HWND
			threadWnd.Attach(pApp->m_pMainWnd->m_hWnd);
			pThread->m_pMainWnd = &threadWnd;
		}
	}
	CATCH_ALL(e)
	{
		// Note: DELETE_EXCEPTION(e) not required.

		// exception happened during thread initialization!!
		TRACE0("Warning: Error during thread initialization!\n");

		// set error flag and allow the creating thread to notice the error
		threadWnd.Detach();
		pStartup->bError = TRUE;
		VERIFY(::SetEvent(pStartup->hEvent));
		AfxEndThread((UINT)-1, FALSE);
		ASSERT(FALSE);  // unreachable
	}
	END_CATCH_ALL

	// pStartup is invaid after the following SetEvent (but hEvent2 is valid)
	HANDLE hEvent2 = pStartup->hEvent2;

	// allow the creating thread to return from CWinThread::CreateThread
	VERIFY(::SetEvent(pStartup->hEvent));

	// wait for thread to be resumed
	VERIFY(::WaitForSingleObject(hEvent2, INFINITE) == WAIT_OBJECT_0);
	::CloseHandle(hEvent2);

	// first -- check for simple worker thread
	DWORD nResult = 0;
	if (pThread->m_pfnThreadProc != NULL)
	{
		nResult = (*pThread->m_pfnThreadProc)(pThread->m_pThreadParams);
		ASSERT_VALID(pThread);
	}
	// else -- check for thread with message loop
	else if (!pThread->InitInstance())
	{
		ASSERT_VALID(pThread);
		nResult = pThread->ExitInstance();
	}
	else
	{
		// will stop after PostQuitMessage called
		ASSERT_VALID(pThread);
		nResult = pThread->Run();
	}

	// cleanup and shutdown the thread
	threadWnd.Detach();
	AfxEndThread(nResult);

	return 0;   // not reached
}

#endif //_MT

CWinThread* AFXAPI AfxGetThread()
{
	// check for current thread in module thread state
	AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
	CWinThread* pThread = pState->m_pCurrentWinThread;

	// if no CWinThread for the module, then use the global app
	if (pThread == NULL)
		pThread = AfxGetApp();

	return pThread;
}

CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,
	int nPriority, UINT nStackSize, DWORD dwCreateFlags,
	LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
	pfnThreadProc;
	pParam;
	nPriority;
	nStackSize;
	dwCreateFlags;
	lpSecurityAttrs;

	return NULL;
#else
	ASSERT(pfnThreadProc != NULL);

	CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);
	ASSERT_VALID(pThread);

	if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,
		lpSecurityAttrs))
	{
		pThread->Delete();
		return NULL;
	}
	VERIFY(pThread->SetThreadPriority(nPriority));
	if (!(dwCreateFlags & CREATE_SUSPENDED))
		VERIFY(pThread->ResumeThread() != (DWORD)-1);

	return pThread;
#endif //!_MT)
}

CWinThread* AFXAPI AfxBeginThread(CRuntimeClass* pThreadClass,
	int nPriority, UINT nStackSize, DWORD dwCreateFlags,
	LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
	pThreadClass;
	nPriority;
	nStackSize;
	dwCreateFlags;
	lpSecurityAttrs;

	return NULL;
#else
	ASSERT(pThreadClass != NULL);
	ASSERT(pThreadClass->IsDerivedFrom(RUNTIME_CLASS(CWinThread)));

	CWinThread* pThread = (CWinThread*)pThreadClass->CreateObject();
	if (pThread == NULL)
		AfxThrowMemoryException();
	ASSERT_VALID(pThread);

	pThread->m_pThreadParams = NULL;
	if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,
		lpSecurityAttrs))
	{
		pThread->Delete();
		return NULL;
	}
	VERIFY(pThread->SetThreadPriority(nPriority));
	if (!(dwCreateFlags & CREATE_SUSPENDED))
		VERIFY(pThread->ResumeThread() != (DWORD)-1);

	return pThread;
#endif //!_MT
}

void AFXAPI AfxEndThread(UINT nExitCode, BOOL bDelete)
{
#ifndef _MT
	nExitCode;
	bDelete;
#else
	// remove current CWinThread object from memory
	AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
	CWinThread* pThread = pState->m_pCurrentWinThread;
	if (pThread != NULL)
	{
		ASSERT_VALID(pThread);
		ASSERT(pThread != AfxGetApp());

		// cleanup OLE if required
		if (pThread->m_lpfnOleTermOrFreeLib != NULL)
			(*pThread->m_lpfnOleTermOrFreeLib)(TRUE, FALSE);

		if (bDelete)
			pThread->Delete();
		pState->m_pCurrentWinThread = NULL;
	}

	// allow cleanup of any thread local objects
	AfxTermThread();

	// allow C-runtime to cleanup, and exit the thread
	_endthreadex(nExitCode);
#endif //!_MT
}

/////////////////////////////////////////////////////////////////////////////
// Global functions for thread initialization and thread cleanup

LRESULT CALLBACK _AfxMsgFilterHook(int code, WPARAM wParam, LPARAM lParam);

void AFXAPI AfxInitThread()
{
	if (!afxContextIsDLL)
	{
		// set message filter proc
		_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
		ASSERT(pThreadState->m_hHookOldMsgFilter == NULL);
		pThreadState->m_hHookOldMsgFilter = ::SetWindowsHookEx(WH_MSGFILTER,
			_AfxMsgFilterHook, NULL, ::GetCurrentThreadId());

#ifndef _MAC
		// intialize CTL3D for this thread
		_AFX_CTL3D_STATE* pCtl3dState = _afxCtl3dState;
		if (pCtl3dState->m_pfnAutoSubclass != NULL)
			(*pCtl3dState->m_pfnAutoSubclass)(AfxGetInstanceHandle());

		// allocate thread local _AFX_CTL3D_THREAD just for automatic termination
		_AFX_CTL3D_THREAD* pTemp = _afxCtl3dThread;
#endif
	}
}

extern CThreadSlotData* _afxThreadData;
void AFXAPI AfxTermThread(HINSTANCE hInstTerm)
{
#ifdef _DEBUG
	// check for missing AfxLockTempMap calls
	if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
	{
		TRACE1("Warning: Temp map lock count non-zero (%ld).\n",
			AfxGetModuleThreadState()->m_nTempMapLock);
	}
#endif
	AfxLockTempMaps();
	AfxUnlockTempMaps();

	// cleanup thread local tooltip window
	if (hInstTerm == NULL)
	{
		_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetDataNA();
		if ((pThreadState != NULL) &&
			(pThreadState->m_pToolTip != NULL))
		{
			pThreadState->m_pToolTip->DestroyWindow();
			delete pThreadState->m_pToolTip;
			pThreadState->m_pToolTip=NULL;
		}
	}

	// cleanup the rest of the thread local data
	if (_afxThreadData != NULL)
		_afxThreadData->DeleteValues(hInstTerm, FALSE);
}

/////////////////////////////////////////////////////////////////////////////
// CWinThread construction

#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif

CWinThread::CWinThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam)
{
	m_pfnThreadProc = pfnThreadProc;
	m_pThreadParams = pParam;

	CommonConstruct();
}

CWinThread::CWinThread()
{
	m_pThreadParams = NULL;
	m_pfnThreadProc = NULL;

	CommonConstruct();
}

void CWinThread::CommonConstruct()
{
	m_pMainWnd = NULL;
	m_pActiveWnd = NULL;

	// no HTHREAD until it is created
	m_hThread = NULL;
	m_nThreadID = 0;

	// initialize message pump
#ifdef _DEBUG
	m_nDisablePumpCount = 0;
#endif
	m_msgCur.message = WM_NULL;
	m_nMsgLast = WM_NULL;
	::GetCursorPos(&m_ptCursorLast);

	// most threads are deleted when not needed
	m_bAutoDelete = TRUE;

	// initialize OLE state
	m_pMessageFilter = NULL;
	m_lpfnOleTermOrFreeLib = NULL;
}

#ifdef AFX_TERM_SEG
#pragma code_seg(AFX_TERM_SEG)
#endif

CWinThread::~CWinThread()
{
	// free thread object
	if (m_hThread != NULL)
		CloseHandle(m_hThread);

	// cleanup module state
	AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
	if (pState->m_pCurrentWinThread == this)
		pState->m_pCurrentWinThread = NULL;
}

#ifdef AFX_CORE1_SEG
#pragma code_seg(AFX_CORE1_SEG)
#endif

BOOL CWinThread::CreateThread(DWORD dwCreateFlags, UINT nStackSize,
	LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
	dwCreateFlags;
	nStackSize;
	lpSecurityAttrs;

	return FALSE;
#else
	ASSERT(m_hThread == NULL);  // already created?

	// setup startup structure for thread initialization
	_AFX_THREAD_STARTUP startup; memset(&startup, 0, sizeof(startup));
	startup.pThreadState = AfxGetThreadState();
	startup.pThread = this;
	startup.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
	startup.hEvent2 = ::CreateEvent(NULL, TRUE, FALSE, NULL);
	startup.dwCreateFlags = dwCreateFlags;
	if (startup.hEvent == NULL || startup.hEvent2 == NULL)
	{
		TRACE0("Warning: CreateEvent failed in CWinThread::CreateThread.\n");
		if (startup.hEvent != NULL)
			::CloseHandle(startup.hEvent);
		if (startup.hEvent2 != NULL)
			::CloseHandle(startup.hEvent2);
		return FALSE;
	}

	// create the thread (it may or may not start to run)
	m_hThread = (HANDLE)_beginthreadex(lpSecurityAttrs, nStackSize,
		&_AfxThreadEntry, &startup, dwCreateFlags | CREATE_SUSPENDED, (UINT*)&m_nThreadID);
	if (m_hThread == NULL)
		return FALSE;

	// start the thread just for MFC initialization
	VERIFY(ResumeThread() != (DWORD)-1);
	VERIFY(::WaitForSingleObject(startup.hEvent, INFINITE) == WAIT_OBJECT_0);
	::CloseHandle(startup.hEvent);

	// if created suspended, suspend it until resume thread wakes it up
	if (dwCreateFlags & CREATE_SUSPENDED)
		VERIFY(::SuspendThread(m_hThread) != (DWORD)-1);

	// if error during startup, shut things down
	if (startup.bError)
	{
		VERIFY(::WaitForSingleObject(m_hThread, INFINITE) == WAIT_OBJECT_0);
		::CloseHandle(m_hThread);
		m_hThread = NULL;
		::CloseHandle(startup.hEvent2);
		return FALSE;
	}

	// allow thread to continue, once resumed (it may already be resumed)
	::SetEvent(startup.hEvent2);
	return TRUE;
#endif //!_MT
}

void CWinThread::Delete()
{
	// delete thread if it is auto-deleting
	if (m_bAutoDelete)
		delete this;
}

/////////////////////////////////////////////////////////////////////////////
// CWinThread default implementation

BOOL CWinThread::InitInstance()
{
	ASSERT_VALID(this);

	return FALSE;   // by default don't enter run loop
}

// main running routine until thread exits
int CWinThread::Run()
{
	ASSERT_VALID(this);

	// for tracking the idle time state
	BOOL bIdle = TRUE;
	LONG lIdleCount = 0;

	// acquire and dispatch messages until a WM_QUIT message is received.
	for (;;)
	{
		// phase1: check to see if we can do idle work
		while (bIdle &&
			!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
		{
			// call OnIdle while in bIdle state
			if (!OnIdle(lIdleCount++))
				bIdle = FALSE; // assume "no idle" state
		}

		// phase2: pump messages while available
		do
		{
			// pump message, but quit on WM_QUIT
			if (!PumpMessage())
				return ExitInstance();

			// reset "no idle" state after pumping "normal" message
			if (IsIdleMessage(&m_msgCur))
			{
				bIdle = TRUE;
				lIdleCount = 0;
			}

		} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
	}

	ASSERT(FALSE);  // not reachable
}

BOOL CWinThread::IsIdleMessage(MSG* pMsg)
{
	// Return FALSE if the message just dispatched should _not_
	// cause OnIdle to be run.  Messages which do not usually
	// affect the state of the user interface and happen very
	// often are checked for.

	// redundant WM_MOUSEMOVE and WM_NCMOUSEMOVE
	if (pMsg->message == WM_MOUSEMOVE || pMsg->message == WM_NCMOUSEMOVE)
	{
		// mouse move at same position as last mouse move?
		if (m_ptCursorLast == pMsg->pt && pMsg->message == m_nMsgLast)
			return FALSE;

		m_ptCursorLast = pMsg->pt;  // remember for next time
		m_nMsgLast = pMsg->message;
		return TRUE;
	}

	// WM_PAINT and WM_SYSTIMER (caret blink)
	return pMsg->message != WM_PAINT && pMsg->message != 0x0118;
}

int CWinThread::ExitInstance()
{
	ASSERT_VALID(this);
	ASSERT(AfxGetApp() != this);

	int nResult = m_msgCur.wParam;  // returns the value from PostQuitMessage
	return nResult;
}

BOOL CWinThread::OnIdle(LONG lCount)
{
	ASSERT_VALID(this);

#if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT)
	// check MFC's allocator (before idle)
	if (_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) & _CRTDBG_CHECK_ALWAYS_DF)
		ASSERT(AfxCheckMemory());
#endif

	if (lCount <= 0)
	{
		// send WM_IDLEUPDATECMDUI to the main window
		CWnd* pMainWnd = m_pMainWnd;
		if (pMainWnd != NULL && pMainWnd->m_hWnd != NULL &&
			pMainWnd->IsWindowVisible())
		{
			AfxCallWndProc(pMainWnd, pMainWnd->m_hWnd,
				WM_IDLEUPDATECMDUI, (WPARAM)TRUE, 0);
			pMainWnd->SendMessageToDescendants(WM_IDLEUPDATECMDUI,
				(WPARAM)TRUE, 0, TRUE, TRUE);
		}
		// send WM_IDLEUPDATECMDUI to all frame windows
		AFX_MODULE_THREAD_STATE* pState = _AFX_CMDTARGET_GETSTATE()->m_thread;
		CFrameWnd* pFrameWnd = pState->m_frameList;
		while (pFrameWnd != NULL)
		{
			if (pFrameWnd->m_hWnd != NULL && pFrameWnd != pMainWnd)
			{
				if (pFrameWnd->m_nShowDelay == SW_HIDE)
					pFrameWnd->ShowWindow(pFrameWnd->m_nShowDelay);
				if (pFrameWnd->IsWindowVisible() ||
					pFrameWnd->m_nShowDelay >= 0)
				{
					AfxCallWndProc(pFrameWnd, pFrameWnd->m_hWnd,
						WM_IDLEUPDATECMDUI, (WPARAM)TRUE, 0);
					pFrameWnd->SendMessageToDescendants(WM_IDLEUPDATECMDUI,
						(WPARAM)TRUE, 0, TRUE, TRUE);
				}
				if (pFrameWnd->m_nShowDelay > SW_HIDE)
					pFrameWnd->ShowWindow(pFrameWnd->m_nShowDelay);
				pFrameWnd->m_nShowDelay = -1;
			}
			pFrameWnd = pFrameWnd->m_pNextFrameWnd;
		}
	}
	else if (lCount >= 0)
	{
		AFX_MODULE_THREAD_STATE* pState = _AFX_CMDTARGET_GETSTATE()->m_thread;
		if (pState->m_nTempMapLock == 0)
		{
			// free temp maps, OLE DLLs, etc.
			AfxLockTempMaps();
			AfxUnlockTempMaps();
		}
	}

#if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT)
	// check MFC's allocator (after idle)
	if (_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) & _CRTDBG_CHECK_ALWAYS_DF)
		ASSERT(AfxCheckMemory());
#endif

	return lCount < 0;  // nothing more to do if lCount >= 0
}

void CWinThread::DispatchThreadMessage(MSG* pMsg)
{
	const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap();
	const AFX_MSGMAP_ENTRY* lpEntry;

#ifdef _AFXDLL
	for (/* pMessageMap already init'ed */; pMessageMap != NULL;
		pMessageMap = (*pMessageMap->pfnGetBaseMap)())
#else
	for (/* pMessageMap already init'ed */; pMessageMap != NULL;
		pMessageMap = pMessageMap->pBaseMap)
#endif

	{
		// Note: catch not so common but fatal mistake!!
		//      BEGIN_MESSAGE_MAP(CMyThread, CMyThread)
#ifdef _AFXDLL
		ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap)());
#else
		ASSERT(pMessageMap != pMessageMap->pBaseMap);
#endif

		if (pMsg->message < 0xC000)
		{
			// constant window message
			if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,
				pMsg->message, 0, 0)) != NULL)
				goto LDispatch;
		}
		else
		{
			// registered windows message
			lpEntry = pMessageMap->lpEntries;
			while ((lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, 0, 0)) != NULL)
			{
				UINT* pnID = (UINT*)(lpEntry->nSig);
				ASSERT(*pnID >= 0xC000);
					// must be successfully registered
				if (*pnID == pMsg->message)
					goto LDispatch;
				lpEntry++;      // keep looking past this one
			}
		}
	}
	return;

LDispatch:
	union MessageMapFunctions mmf;
	mmf.pfn = lpEntry->pfn;

	// always posted, so return value is meaningless

	(this->*mmf.pfn_THREAD)(pMsg->wParam, pMsg->lParam);
	return;
}

BOOL CWinThread::PreTranslateMessage(MSG* pMsg)
{
	ASSERT_VALID(this);

	// if this is a thread-message, short-circuit this function
	if (pMsg->hwnd == NULL)
	{
		DispatchThreadMessage(pMsg);
		return TRUE;
	}

	UINT message = pMsg->message;
	BOOL bKeys = (message >= WM_KEYFIRST && message <= WM_KEYLAST) ||
		(message >= WM_SYSKEYFIRST && message <= WM_SYSKEYLAST);
	if (bKeys ||
		(message == WM_LBUTTONDOWN || message == WM_LBUTTONDBLCLK) ||
		(message == WM_RBUTTONDOWN || message == WM_RBUTTONDBLCLK) ||
		(message == WM_MBUTTONDOWN || message == WM_MBUTTONDBLCLK) ||
		(message == WM_NCLBUTTONDOWN || message == WM_NCLBUTTONDBLCLK) ||
		(message == WM_NCRBUTTONDOWN || message == WM_NCRBUTTONDBLCLK) ||
		(message == WM_NCMBUTTONDOWN || message == WM_NCMBUTTONDBLCLK))
	{
		CWnd::CancelToolTips(bKeys);
	}

	// walk from target to main window
	CWnd* pMainWnd = AfxGetMainWnd();
	if (CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg))
		return TRUE;

	// in case of modeless dialogs, last chance route through main
	//   window's accelerator table
	if (pMainWnd != NULL)
	{
		 CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd);
		 if (pWnd->GetTopLevelParent() != pMainWnd)
			return pMainWnd->PreTranslateMessage(pMsg);
	}

	return FALSE;   // no special processing
}

LRESULT CWinThread::ProcessWndProcException(CException*, const MSG* pMsg)
{
	if (pMsg->message == WM_CREATE)
	{
		return -1;  // just fail
	}
	else if (pMsg->message == WM_PAINT)
	{
		// force validation of window to prevent getting WM_PAINT again
		ValidateRect(pMsg->hwnd, NULL);
		return 0;
	}
	return 0;   // sensible default for rest of commands
}

/////////////////////////////////////////////////////////////////////////////
// Message Filter processing (WH_MSGFILTER)

LRESULT CALLBACK _AfxMsgFilterHook(int code, WPARAM wParam, LPARAM lParam)
{
	CWinThread* pThread;
	if (afxContextIsDLL || (code < 0 && code != MSGF_DDEMGR) ||
		(pThread = AfxGetThread()) == NULL)
	{
		return ::CallNextHookEx(_afxThreadState->m_hHookOldMsgFilter,
			code, wParam, lParam);
	}
	ASSERT(pThread != NULL);
	return (LRESULT)pThread->ProcessMessageFilter(code, (LPMSG)lParam);
}

static BOOL AFXAPI IsHelpKey(LPMSG lpMsg)
	// return TRUE only for non-repeat F1 keydowns.
{
	return lpMsg->message == WM_KEYDOWN &&
#ifndef _MAC
		   lpMsg->wParam == VK_F1 &&
#else
		   lpMsg->wParam == VK_HELP &&
#endif
		   !(HIWORD(lpMsg->lParam) & KF_REPEAT) &&
		   GetKeyState(VK_SHIFT) >= 0 &&
		   GetKeyState(VK_CONTROL) >= 0 &&
		   GetKeyState(VK_MENU) >= 0;
}

static inline BOOL IsEnterKey(LPMSG lpMsg)
	{ return lpMsg->message == WM_KEYDOWN && lpMsg->wParam == VK_RETURN; }

static inline BOOL IsButtonUp(LPMSG lpMsg)
	{ return lpMsg->message == WM_LBUTTONUP; }

BOOL CWinThread::ProcessMessageFilter(int code, LPMSG lpMsg)
{
	if (lpMsg == NULL)
		return FALSE;   // not handled

	CFrameWnd* pTopFrameWnd;
	CWnd* pMainWnd;
	CWnd* pMsgWnd;
	switch (code)
	{
	case MSGF_DDEMGR:
		// Unlike other WH_MSGFILTER codes, MSGF_DDEMGR should
		//  never call the next hook.
		// By returning FALSE, the message will be dispatched
		//  instead (the default behavior).
		return FALSE;

	case MSGF_MENU:
		pMsgWnd = CWnd::FromHandle(lpMsg->hwnd);
		if (pMsgWnd != NULL)
		{
			pTopFrameWnd = pMsgWnd->GetTopLevelFrame();
			if (pTopFrameWnd != NULL && pTopFrameWnd->IsTracking() &&
				pTopFrameWnd->m_bHelpMode)
			{
				pMainWnd = AfxGetMainWnd();
				if ((m_pMainWnd != NULL) && (IsEnterKey(lpMsg) || IsButtonUp(lpMsg)))
				{
					pMainWnd->SendMessage(WM_COMMAND, ID_HELP);
					return TRUE;
				}
			}
		}
		// fall through...

	case MSGF_DIALOGBOX:    // handles message boxes as well.
		pMainWnd = AfxGetMainWnd();
		if (afxData.nWinVer < 0x333 && pMainWnd != NULL && IsHelpKey(lpMsg))
		{
			pMainWnd->SendMessage(WM_COMMAND, ID_HELP);
			return TRUE;
		}
		if (code == MSGF_DIALOGBOX && m_pActiveWnd != NULL &&
			lpMsg->message >= WM_KEYFIRST && lpMsg->message <= WM_KEYLAST)
		{
			// need to translate messages for the in-place container
			_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
			if (pThreadState->m_bInMsgFilter)
				return FALSE;
			pThreadState->m_bInMsgFilter = TRUE;    // avoid reentering this code
			MSG msg = *lpMsg;
			if (m_pActiveWnd->IsWindowEnabled() && PreTranslateMessage(&msg))
			{
				pThreadState->m_bInMsgFilter = FALSE;
				return TRUE;
			}
			pThreadState->m_bInMsgFilter = FALSE;    // ok again
		}
		break;
	}

	return FALSE;   // default to not handled
}

/////////////////////////////////////////////////////////////////////////////
// Access to m_pMainWnd & m_pActiveWnd

CWnd* CWinThread::GetMainWnd()
{
	if (m_pActiveWnd != NULL)
		return m_pActiveWnd;    // probably in-place active

	// when not inplace active, just return main window
	if (m_pMainWnd != NULL)
		return m_pMainWnd;

	return CWnd::GetActiveWindow();
}

/////////////////////////////////////////////////////////////////////////////
// CWinThread implementation helpers

BOOL CWinThread::PumpMessage()
{
	ASSERT_VALID(this);

	if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
	{
#ifdef _DEBUG
		if (afxTraceFlags & traceAppMsg)
			TRACE0("CWinThread::PumpMessage - Received WM_QUIT.\n");
		m_nDisablePumpCount++; // application must die
			// Note: prevents calling message loop things in 'ExitInstance'
			// will never be decremented
#endif
		return FALSE;
	}

#ifdef _DEBUG
	if (m_nDisablePumpCount != 0)
	{
		TRACE0("Error: CWinThread::PumpMessage called when not permitted.\n");
		ASSERT(FALSE);
	}
#endif

#ifdef _DEBUG
	if (afxTraceFlags & traceAppMsg)
		_AfxTraceMsg(_T("PumpMessage"), &m_msgCur);
#endif

	// process this message

	if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
	{
		::TranslateMessage(&m_msgCur);
		::DispatchMessage(&m_msgCur);
	}
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CWinThread diagnostics

#ifdef _DEBUG
void CWinThread::AssertValid() const
{
	CCmdTarget::AssertValid();
}

void CWinThread::Dump(CDumpContext& dc) const
{
	CCmdTarget::Dump(dc);

	dc << "m_pThreadParams = " << m_pThreadParams;
	dc << "\nm_pfnThreadProc = " << (void*)m_pfnThreadProc;
	dc << "\nm_bAutoDelete = " << m_bAutoDelete;
	dc << "\nm_hThread = " << (void*)m_hThread;
	dc << "\nm_nThreadID = " << m_nThreadID;
	dc << "\nm_nDisablePumpCount = " << m_nDisablePumpCount;
	if (AfxGetThread() == this)
		dc << "\nm_pMainWnd = " << m_pMainWnd;

	dc << "\nm_msgCur = {";
	dc << "\n\thwnd = " << (UINT)m_msgCur.hwnd;
	dc << "\n\tmessage = " << (UINT)m_msgCur.message;
	dc << "\n\twParam = " << (UINT)m_msgCur.wParam;
	dc << "\n\tlParam = " << (void*)m_msgCur.lParam;
	dc << "\n\ttime = " << m_msgCur.time;
	dc << "\n\tpt = " << CPoint(m_msgCur.pt);
	dc << "\n}";

	dc << "\nm_pThreadParams = " << m_pThreadParams;
	dc << "\nm_pfnThreadProc = " << (void*)m_pfnThreadProc;
	dc << "\nm_ptCursorLast = " << m_ptCursorLast;
	dc << "\nm_nMsgLast = " << m_nMsgLast;

	dc << "\n";
}
#endif

#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif

IMPLEMENT_DYNAMIC(CWinThread, CCmdTarget)

/////////////////////////////////////////////////////////////////////////////