Click here to Skip to main content
15,891,864 members
Articles / Web Development / HTML

A Comprehensive CE Class Library to Replace ATL and MFC

Rate me:
Please Sign up or sign in to vote.
4.48/5 (14 votes)
4 Oct 2000CPOL 280.8K   998   70  
A collection of classes for CE that do not use ATL or MFC, plus an FTP client, database viewer, and sample application that solves beam deflection equations.
#ifndef __Thread_h__
#define __Thread_h__

//#ifdef _WIN32_WCE
#include "CeDebug.h"
//#endif

#if !defined(_WIN32_WCE) && !defined(_WIN32_DCOM)
# error DCOM must be defined when not in Windows CE
#endif

#include <objidl.h>

#if !defined(_ATL_MIN_CRT) && !defined(_WIN32_WCE)
#include <process.h>
#endif

#ifndef WAIT_OBJECT_1
#define WAIT_OBJECT_1 (WAIT_OBJECT_0 + 1)
#define WAIT_OBJECT_2 (WAIT_OBJECT_0 + 2)
#define WAIT_OBJECT_3 (WAIT_OBJECT_0 + 3)
#endif

#pragma warning(disable: 4018) // signed/unsigned

#ifndef _WIN32_WCE
class CeInProcMarshal
{
private:
	LPSTREAM m_pStream;
	IID m_iidMarshalled;
public:
	CeInProcMarshal() { m_pStream = NULL; }
	~CeInProcMarshal()
	{
		if (NULL != m_pStream)
		{
			IUnknown* pUnk = NULL;
			TRACE(_T("CInProcMarshal: Interface Stream never retrieved, will release now!\n"));
			if (S_OK == Get(m_iidMarshalled, (void**) &pUnk))
				pUnk->Release();
		}
	}

	HRESULT Set(REFIID riid, LPUNKNOWN pUnk)
	{
		if (NULL == pUnk)
			return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);

		HRESULT hr = ::CoMarshalInterThreadInterfaceInStream(riid, pUnk, &m_pStream);
//		if (! SUCCEEDED(hr))
//			TRACE("CoMarshalInterThreadInterfaceInStream failed\n");

		// keep a copy until I can figure out a way to get it from the stream
		m_iidMarshalled = riid;

		return hr;
	}

	HRESULT Get(REFIID riid, LPVOID * ppv)
	{
		if (NULL == m_pStream)
			return S_FALSE;

		if (m_iidMarshalled != riid)
			return E_NOINTERFACE;

		HRESULT hr = ::CoGetInterfaceAndReleaseStream(m_pStream, riid, ppv);
		if (! SUCCEEDED(hr))
			TRACE(_T("CoGetInterfaceAndReleaseStream failed\n"));
		else
			m_pStream = NULL;

		return hr;
	}
};
#endif // ! _WIN32_WCE


class CeCriticalSection
{
public:
	CRITICAL_SECTION m_cs;

	CeCriticalSection() { ::InitializeCriticalSection(&m_cs); }
	~CeCriticalSection() { ::DeleteCriticalSection(&m_cs); }
	operator CRITICAL_SECTION() { return m_cs; }
	void Enter() { ::EnterCriticalSection(&m_cs); }
	void Leave() { ::LeaveCriticalSection(&m_cs); }
	void Lock() { Enter(); }
	void Unlock() { Leave(); }
#if(_WIN32_WINNT >= 0x0401)
	BOOL TryLock() { return ::TryEnterCriticalSection(&m_cs); }
	BOOL TryEnter() { return ::TryEnterCriticalSection(&m_cs); }
#endif
};

class CeWaitableHandle
{
public:
	HANDLE m_handle;
	operator HANDLE()
		{ return m_handle; }

	HANDLE GetHandle() const
		{ return m_handle; }

	DWORD WaitFor(DWORD dwMilliseconds=INFINITE)
		{ return ::WaitForSingleObject(m_handle, dwMilliseconds); }

	DWORD MsgWaitFor(DWORD dwMilliseconds=INFINITE)
	{
		DWORD dwWait;
		while (WAIT_OBJECT_0 + 1 == (dwWait = ::MsgWaitForMultipleObjects(1, &m_handle, FALSE, dwMilliseconds, QS_ALLINPUT)))
		{
			// message to process, process and continue
			MSG msg;

			// just to be careful, i've run into an unexplainable problem
			// where the ::MsgWait... returned with this value, but the
			// GetMessage() hung waiting for a message, so just check first
			if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
				DispatchMessage(&msg);
		}
		return dwWait;
	}

	BOOL Close()
	{
		if (NULL == m_handle)
			return FALSE;
		BOOL bRet = ::CloseHandle(m_handle);
		m_handle = NULL;
		return bRet;
	}
};


//
// Manual reset event, requires Reset in order to unsignal the event
//
class CeManualEvent: public CeWaitableHandle
{
public:
	CeManualEvent(BOOL bSet=FALSE)
	{
		m_handle = ::CreateEvent(NULL, TRUE, bSet, NULL);
	}
	CeManualEvent(LPCTSTR szName, BOOL bSet, LPSECURITY_ATTRIBUTES psa=NULL)
	{
		m_handle = ::CreateEvent(psa, TRUE, bSet, szName);
	}

	~CeManualEvent() { Close(); }
	void Set()		{ ::SetEvent(m_handle); }
	void Reset()	{ ::ResetEvent(m_handle); }
	void Pulse()	{ ::PulseEvent(m_handle); }
	BOOL IsSet()	{ return (WAIT_OBJECT_0 == WaitFor(0)); }
};


//
// Auto reset event, the event is reset once a SINGLE waiting thread
// is release from a wait state
//
class CeAutoEvent: public CeWaitableHandle
{
public:
	CeAutoEvent(BOOL bSet=FALSE)
	{
		m_handle = ::CreateEvent(NULL, FALSE, bSet, NULL);
	}
	CeAutoEvent(LPCTSTR szName, BOOL bSet=FALSE, LPSECURITY_ATTRIBUTES psa=NULL)
	{
		m_handle = ::CreateEvent(psa, FALSE, bSet, szName);
	}

	~CeAutoEvent()	{   Close(); }
	void Set()		{ ::SetEvent(m_handle); }
	void Reset()	{ ::ResetEvent(m_handle); }
	void Pulse()	{ ::PulseEvent(m_handle); }
};


#ifndef _WIN32_WCE
class CeSemaphore: public CeWaitableHandle
{
public:
	CeSemaphore(LONG lMax)
	{
		m_handle = ::CreateSemaphore(NULL, 0, lMax, NULL);
	}
	~CeSemaphore() { Close(); }
	void Release()		{ ::ReleaseSemaphore(m_handle, 1, NULL); }
	LONG Release(LONG lIncr)
	{
		LONG lOldCnt;
		::ReleaseSemaphore(m_handle, lIncr, &lOldCnt);
		return lOldCnt;
	}
};
#endif //_WIN32_WCE


class CeWaitObjects
{
private:
	HANDLE* m_pHandleArray;
	int m_nHandles;

public:
	// empty wait object group
	CeWaitObjects()
	{
		m_pHandleArray = NULL;
		m_nHandles = 0;
	}
	// Simple two item wait
	CeWaitObjects(CeWaitableHandle* pHandle1, CeWaitableHandle* pHandle2)
	{
		_ASSERTE(NULL != pHandle1);
		_ASSERTE(NULL != pHandle2);
		m_pHandleArray = new HANDLE[2];
		m_nHandles = 2;
		m_pHandleArray[0] = (HANDLE) *pHandle1;
		m_pHandleArray[1] = (HANDLE) *pHandle2;
	}
	CeWaitObjects(CeWaitableHandle* pHandleArray, int nHandles)
	{
		m_pHandleArray = new HANDLE[nHandles];
		m_nHandles = nHandles;

		for (int ii = 0; ii < nHandles; ii++)
		{
			_ASSERTE(NULL != m_pHandleArray[ii]);
			m_pHandleArray[ii] = pHandleArray[ii];
		}
	}

	BOOL Add(HANDLE h)
	{
		HANDLE* pTmp = new HANDLE[m_nHandles+1];
		if (! pTmp)
			return FALSE;

		pTmp[m_nHandles] = h;

		if (m_pHandleArray)
		{
			memcpy(pTmp, m_pHandleArray, m_nHandles*sizeof(HANDLE));
			delete[] m_pHandleArray;
		}

		m_pHandleArray = pTmp;
		m_nHandles++;
		return TRUE;
	}

	~CeWaitObjects()
	{
		if (m_pHandleArray)
			delete [] m_pHandleArray;
	}

	DWORD WaitFor(DWORD dwTimeOut=INFINITE, BOOL bWaitForAll=FALSE, DWORD dwWakeMask=0)
	{
		// number of handles in the object handle array 
		DWORD dwResult;
		if (dwWakeMask == 0)
			dwResult = ::WaitForMultipleObjects(m_nHandles,
				m_pHandleArray, bWaitForAll, dwTimeOut);
		else
			dwResult = ::MsgWaitForMultipleObjects(m_nHandles,
				m_pHandleArray, bWaitForAll, dwTimeOut, dwWakeMask);

//		if (WAIT_FAILED == dwResult)
//			TRACE("Error: %u\n", GetLastError());

		return dwResult;
	}

	DWORD MsgWaitFor(DWORD dwTimeOut=INFINITE, BOOL bWaitForAll=FALSE)
	{
		// wait until a NON-windows message event wakes up the object
		DWORD dwWait;
		while (WAIT_OBJECT_0 + m_nHandles == (dwWait = ::MsgWaitForMultipleObjects(m_nHandles, m_pHandleArray, bWaitForAll, dwTimeOut, QS_ALLINPUT)))
		{
			// message to process, process and continue
			MSG msg;

			// just to be careful, i've run into an unexplainable problem
			// where the ::MsgWait... returned with this value, but the
			// GetMessage() hung waiting for a message, so just check first
			if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
				DispatchMessage(&msg);
		}

		return dwWait;
	}
}; 

#define WM_WORKER_DONE (WM_USER + 0x400)

// Note:
// This class does NOT work properly if ExitThread() is called from within the thread
// instead you must

class CeWorkerThread: public CeWaitableHandle
{
protected:
	bool m_bRunning;
	bool m_bAutoDelete;
	bool m_bStopping;

public:
	static DWORD WINAPI StartThread(LPVOID pParam);

	HWND m_hWndMsg;
	DWORD m_dwThreadId;
	CeManualEvent m_eventStop;
	DWORD m_dwStopTimeOut;

	bool m_bInitCOM;
	COINIT m_nCoInit;

	CeWorkerThread(BOOL bAutoDelete=FALSE, HWND hWndMsg=NULL, DWORD dwStopTimeOut=INFINITE);
	~CeWorkerThread();

// Methods used by derived classes
	virtual void Delete();
	virtual void Exit(DWORD dwExitCode);
	BOOL IsStopSet() const { return m_bStopping; }

// Methods used by other threads
	virtual BOOL Start(int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
	virtual void Stop(BOOL bDispatchMsgs=FALSE);

	DWORD WaitForComplete();
	BOOL IsRunning() const;

	virtual BOOL PostThreadMessage(UINT Msg, WPARAM wParam = 0, LPARAM lParam = 0) const;
	virtual BOOL PostWindowMessage(UINT Msg, WPARAM wParam = 0, LPARAM lParam = 0) const;
	virtual BOOL Kill(DWORD dwExit = 1);
	DWORD Pause();
	DWORD Suspend();
	DWORD Resume();
	DWORD GetExitCode();

// Method calls made by either
	int GetPriority() const;
	BOOL SetPriority(int nPriority);
	DWORD GetId() const;
	HANDLE GetHandle() const;

	// just run the thread function directly, debug stuff
	void RunInline();

	// override this to make the thread do something (required) ...
	virtual UINT ThreadProc() = 0;

	// Some virtual calls that happen for the convenience of the
	// derived class, this allows the thread to do some state
	// management if that would aid it in some way
	virtual void OnStop() {}
	virtual void OnSuspend() {}
	virtual void OnResume() {}
	virtual void OnKill() {}
};


#ifdef __AFX_H__

//
// Add MFC support to the thread class
//

class CeMFCWorkerThread : public CeWorkerThread
{
public:
	CeMFCWorkerThread()
	{
		m_pThread = NULL;
	}

	~CeMFCWorkerThread();

	CWinThread* m_pThread;

	// these methods are required in order for MFC to function properly,
	// the rest of the methods defined for CWorkerThread work properly
	// on the handle set up by the Start() method
	virtual BOOL Start( int nPriority = THREAD_PRIORITY_NORMAL,
						UINT nStackSize = 0,
						DWORD dwCreateFlags = 0,
						LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
					  );
	virtual void Delete();
	virtual void Exit(DWORD dwExitCode);
	virtual BOOL Close();
};


inline CeMFCWorkerThread::~CeMFCWorkerThread()
{
	// we ALWAYS hang on to the pointer when we're not auto deleting
	// so we need to get rid of it now, or else we leak CWinThread objects
	if (NULL != m_pThread && ! m_bAutoDelete)
	{
		// if we hung on to the pointer, then we need to get rid of it manually
		m_pThread->m_bAutoDelete = TRUE;
		m_pThread->Delete();

		m_pThread = NULL;
		m_dwThreadId = 0;
		m_handle = NULL;
	}
}


inline BOOL CeMFCWorkerThread::Start(int nPriority, UINT nStackSize, DWORD dwCreateFlags, LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
	// Note: MFC actually starts the thread AFTER calling it's own
	// initalization and suspending the thread and returning this pointer
	// See the MFC source code for details

	m_pThread = AfxBeginThread((AFX_THREADPROC) StartThread,
		this, nPriority, nStackSize, dwCreateFlags, lpSecurityAttrs);

	if (NULL == m_pThread)
		return FALSE;

	// have the MFC thread take on the attributes of our thread
	// implementation
	m_pThread->m_bAutoDelete = m_bAutoDelete;
	m_handle = m_pThread->m_hThread;
	m_dwThreadId = m_pThread->m_nThreadID;

	return TRUE;
}

inline void CeMFCWorkerThread::Delete()
{
	if (m_bAutoDelete && m_pThread)
	{
		m_pThread->m_bAutoDelete = TRUE;
		m_pThread->Delete();

		// at this point just forget about the MFC thread, 
		// MFC is still using is after calling us, we'll let MFC handle
		// this one also
		m_pThread = NULL;
		m_handle = NULL;
		m_dwThreadId = 0;
	}

	CeWorkerThread::Delete();
}

inline void CeMFCWorkerThread::Exit(DWORD dwExitCode)
{
	if (m_bAutoDelete)
	{
		// get rid of our pointer states, MFC will shortly
		m_pThread = NULL;
		m_handle = NULL;
		m_dwThreadId = 0;
	}

	// matchs AfxBeginThread() when aborting the thread
	AfxEndThread(dwExitCode);
}

inline BOOL CeMFCWorkerThread::Close()
{
	// do nothing, the base class gets involved and tries to close
	// the handle and MFC and this class lose
	// After a Close() operation the handles are invalid but
	// the thread still runs, the only problem comes from
	// not being able to wait on completion or get status of any kind
	// so, we'll just null our copy and let MFC take care
	// of the other later
	if (m_pThread)
	{
		if (! m_pThread->m_bAutoDelete)
			// turn on auto delete since we can't deal with the
			// thread anymore
			m_pThread->m_bAutoDelete = TRUE;
		m_pThread = NULL;
	}

	m_dwThreadId = 0;
	m_handle = NULL;

	return TRUE;
}

#endif // __AFX_H__


/////////////////////////////////////////////////////////////////////////////
// inline function definitions

inline DWORD WINAPI CeWorkerThread::StartThread(LPVOID pParam)
{
	UINT iRet = 1;		// assume fatal unless otherwise changed

	__try
	{
		CeWorkerThread* pThis = (CeWorkerThread *) pParam;

		if (0 == pThis->m_dwThreadId)
			// paranoia, in case the creator didn't
			pThis->m_dwThreadId = GetCurrentThreadId();

		if (pThis->m_bInitCOM)
			// requires DCOM or WinNT 4.0
			::CoInitializeEx(NULL, pThis->m_nCoInit);

		pThis->m_bRunning = true;

		// This is the overridable virtual function that
		// is the equivelent to the thread proc
		iRet = pThis->ThreadProc();

		// thread processing complete, do all we can to notify
		// that we have completed processing

		pThis->m_bRunning = false;										// flag for our or others uses
		pThis->PostWindowMessage(WM_WORKER_DONE, iRet, pThis->m_dwThreadId);	// post to associated window

		if (pThis->m_bInitCOM)
			::CoUninitialize();

		// note: this only deletes the thread if autodelete is set
		pThis->Delete();
	}
    __except (EXCEPTION_EXECUTE_HANDLER)
	{
		::MessageBox(NULL, _T("Thread termainated abnormally by an exception"),
			_T("Fatal"), MB_OK | MB_ICONSTOP);
	}

	return iRet;
}


inline void CeWorkerThread::Exit(DWORD dwExitCode)
{
	// thread processing complete, do all we can to notify
	// that we have completed processing
	m_bRunning = false;					// flag for our or others uses
	PostWindowMessage(WM_WORKER_DONE);	// post to associated window

	if (m_bInitCOM)
		::CoUninitialize();

	// note: this only deletes the thread if autodelete is set
	Delete();
	
#if defined(_MT) && !defined(_WIN32_WCE)
	_endthreadex(dwExitCode);
#else
	::ExitThread(dwExitCode);
#endif
}

inline CeWorkerThread::CeWorkerThread(BOOL bAutoDelete/*=FALSE*/, HWND hWndMsg/*=NULL*/, DWORD dwStopTimeOut/*=INFINITE*/)
{
	m_handle = NULL;
	m_hWndMsg = hWndMsg;
	m_dwThreadId = 0;
	m_bRunning = false;
	m_bInitCOM = true;
	m_bStopping = false;
	m_nCoInit = (COINIT) COINIT_MULTITHREADED;
	m_dwStopTimeOut = dwStopTimeOut;
	m_bAutoDelete = (bAutoDelete != FALSE);
}

inline CeWorkerThread::~CeWorkerThread()
{
	Stop();
	Close();
}

inline void CeWorkerThread::RunInline()
{
	StartThread(this);
}


inline BOOL CeWorkerThread::Start(int nPriority, UINT nStackSize, DWORD dwCreateFlags, LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
	if (m_bRunning)
	{
//		TRACE("Start: Thread is already executing, create a new instance if this is what is required!\n");
		// already running
		return FALSE;
	}
#if defined(_MT) && !defined(_WIN32_WCE)
	m_handle = (HANDLE) _beginthreadex(lpSecurityAttrs, nStackSize, 
		(unsigned (__stdcall *) (void *)) StartThread,
		(LPVOID) this, dwCreateFlags,
		(unsigned int*) &m_dwThreadId);
#else
	m_handle = ::CreateThread(lpSecurityAttrs, nStackSize, StartThread,
		(LPVOID) this, dwCreateFlags, &m_dwThreadId);
#endif

	if (m_handle)
		::SetThreadPriority(m_handle, nPriority);

	return (m_handle != NULL);
}

inline void CeWorkerThread::Stop(BOOL bDispatchMsgs/*=FALSE*/)
{
	// set the stop event for the thread to see
	m_eventStop.Set();
	m_bStopping = true;

	// anything to wait for? may have exited already
	if (! m_bRunning || NULL == m_handle)
		return;

	// wait until complete, kill if we timeout,
	// the default implementation waits for infinite time
	// and the Kill() will never get called in that case

	// notify the derived class that we've been asked to stop,
	// this will allow the thread to kill any blocking functions
	// so that it can process the stop request
	OnStop();

	// don't wait if we have autodelete set, since this
	// will just cause a protection fault anyway in the waitfor

	if (m_bAutoDelete)
		// will delete object on return, thus you CANNOT wait on it
		return;

	if (bDispatchMsgs)
	{
		// for GUI threads, get and dispatch messages while waiting
		// for the thread to terminate
		if (WAIT_TIMEOUT == MsgWaitFor(m_dwStopTimeOut))
			// wait for the time out, kill the thread
			Kill();
	}
	else
	{
		if (WAIT_TIMEOUT == WaitFor(m_dwStopTimeOut))
			// wait for the time out, kill the thread
			Kill();
	}
}

inline void CeWorkerThread::Delete()
{
	if (m_bAutoDelete)
		delete this;
}

inline DWORD CeWorkerThread::WaitForComplete()
{
	_ASSERTE(! m_bAutoDelete);
	return WaitFor(INFINITE);
}

inline BOOL CeWorkerThread::IsRunning() const
{
	return m_bRunning;
}
inline int CeWorkerThread::GetPriority() const
{
	return ::GetThreadPriority(m_handle);
}

inline BOOL CeWorkerThread::SetPriority(int nPriority)
{
	return ::SetThreadPriority(m_handle, nPriority);
}

inline DWORD CeWorkerThread::GetId() const
{
	return m_dwThreadId;
}

inline BOOL CeWorkerThread::PostThreadMessage(UINT Msg, WPARAM wParam, LPARAM lParam) const
{
	if (m_dwThreadId)
		return ::PostThreadMessage(m_dwThreadId, Msg, wParam, lParam);
	return FALSE;
} 

inline BOOL CeWorkerThread::PostWindowMessage(UINT Msg, WPARAM wParam, LPARAM lParam) const
{
	if (m_hWndMsg)
		return ::PostMessage(m_hWndMsg, Msg, wParam, lParam);
	return FALSE;
} 

inline BOOL CeWorkerThread::Kill(DWORD dwExit)
{
	OnKill();
	return ::TerminateThread(m_handle, dwExit);
}

inline DWORD CeWorkerThread::Pause()
{
	return Suspend();
}

inline DWORD CeWorkerThread::Suspend()
{
	OnSuspend();
	return ::SuspendThread(m_handle);
}

inline DWORD CeWorkerThread::Resume()
{
	OnResume();
	return ::ResumeThread(m_handle);
}

inline DWORD CeWorkerThread::GetExitCode()
{
	DWORD dwRet;
	if (! ::GetExitCodeThread(m_handle, &dwRet))
		return 0xffffffff;

	if (STILL_ACTIVE == dwRet)
		TRACE(_T("Thread Still Active\n"));
	else
		TRACE(_T("Thread Exit Code %lu\n"), dwRet);

	return dwRet;

}

#endif // Thread_h__

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions