//Thread.h #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #ifndef Threadh #define Threadh /* CThread is a abstract class that allows a derived class to run a Main() function in a separate Thread. This is the shortest and safest code to use and extend for the control of Threads. I use ::CreateThread because you don't need to use DuplicateHandle to get the Threads Handle and you don't need a Window to create the Thread. I create my own implementation of Sleep(Time) which uses MsgWaitForMultipleObjects to avoid hanging and deadlocking. When the class gets destroyed the destructor offers any running Thread the chance to exit gracefully. If a Thread doesn't Stop within 3 seconds of being told to, it is considered hung and gets Terminated. To use it, put the code that is to run in a separate Thread in a class derived from CThread and impement a void Main(): Here's an example for a CDialog: class CFManClientDlg : public CDialog, public CThread { public: CFManClientDlg(CWnd* pParent = NULL) {Start();} // <--This starts the new Thread running the Main() function; void Main() { ... } ... }; This example makes a Window with a counting TitleBar: class CMyThread : public CThread { CWnd* Owner; public: CMyThread(CWnd* pWnd) : Owner(pWnd) {} void Main() { CString S, Old; Owner->GetWindowText(Old); int i=0; while(!Abort) { // we must exit quickly and safely when Abort is true. S.Format("%i", ++i); Owner->SetWindowText(S); Sleep(200); } // for(;;); // hang the thread to test TerminateThread. If you uncomment this and click [Start] or [Stop] you'll get Memory leaks (being the CStrings [S] and [Old]) because the Thread was Terminated. if(::IsWindow(Owner->m_hWnd)) Owner->SetWindowText(Old); } }; To use CMythread, create a Dialog Application (called ThreadTester in this example) with a couple of buttons [Start] and [Stop]. Int the dialog header put the above CMyThread class definition and an #include to this file, and: virtual ~CThreadTesterDlg() {try{delete Thread;}catch(...){ASSERT(0);}} CMyThread* Thread; In the .cpp file add these lines to OnInitDialog(): Thread=new CMyThread(this); Thread->Start(); Then use ClassWizard to add button Handlers for the Start and Stop Buttons and make them look like this: void CThreadTesterDlg::OnStart() {if(!Thread->Start()) Thread->Main();} // If we can't do it in a separate Thread, do it in this one. void CThreadTesterDlg::OnStop () {if(!Thread->Stop ()) SetWindowText(Thread->IsRunning() ? "Runaway Thread!" : "Thread had hung!");} When you click [Start] the Dialog's Title Bar will start counting. When you click [Stop] the Dialog's Title Bar will be restored to the Application's Title. Clicking [Start] is the same as clicking [Stop][Start]. */ class __declspec(novtable) CThread { volatile HANDLE hThread; volatile DWORD ThreadID; protected: virtual void Main()=0; // This is all you have to implement in your derived class. volatile bool Abort; // Your Main() function should watch this variable and exit quickly and safely when Abort is true. public: CThread() : Abort(false), hThread(0) {} virtual ~CThread() {try{Stop();}catch(...){ASSERT(0);}} virtual bool Start(bool Pump=false) { // Don't call this from a derived class's Constructor (The virtual table to the Pure Virtual Main won't exist). if(IsRunning()) Stop(); Abort=false; if(!Pump) return Go(); bool OK=false; // if Pump is true run the Thread and stay in a message loop to keep windows active: for(OK=Go(); !Abort; MessagePump()) Wait(); return OK; } inline bool IsRunning() const {return hThread!=0;} // Could use GetExitCodeThread=STILL_ACTIVE but this way is quicker. virtual bool Stop() { // Returns false if The Thread had hung. If IsRunning() is still true the Thread could not be terminated (Call ::GetLastError(...) to find out why not). if(!IsRunning()) return true; Abort=true; PostMessage(0,WM_QUIT,0,0); if(Sleep(3000)) { // If the Thread is still around in 3 seconds it is assumed hung and gets Terminated: #ifdef _DEBUG CString S; S.Format("TerminateThread(0x%X, 1); called!!!\r\n", hThread); Beep(392, 50); Beep(392, 50); Beep(262, 100); Beep(196, 250); AfxMessageBox(S); #endif if(TerminateThread(hThread, 1)) {CloseHandle(hThread); hThread=0;} // Kill the Thread and exit with code 1 to indicate that the Thread was hung and the system may now be broken depending on what the thread was doing. return false; // Couldn't Stop the Thread! Call ::GetLastError(...) to find out why. } return true; } inline bool Sleep(DWORD Time) {return Sleep((HANDLE)hThread, Time);} // Since this is a safer Sleep than the system one, I'll make it available as a static: static bool Sleep(HANDLE& hThread, DWORD Time) { // This is the clever bit that no one tells you ;-) for(DWORD Now=GetTickCount(); hThread && ((GetTickCount()-Now)Run(); return 0;} // New Thread starts here. Redirect back to the class instance that started the new Thread. inline void Run() {AFX_MANAGE_STATE(AfxGetStaticModuleState()); Main(); Abort=true; CloseHandle(hThread); hThread=0;} // The assignments indicate that the Thread has terminated. }; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> CLock <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< This class stops two different threads from entering the same code section at the same time. Most locking using CThread can be done using: if(!IsRunning()) Start(); because you will usually have one Thread per function. Sometimes your Thread will get more complicated and that's when you use CLock... Usage: class MyClass { CLock Lock; MyClass() {if(!Lock.Lockable() Panic();} void MySub1() { if(!Lock.Lock()) return; ... Lock.Unlock(); } void MySub2() { CLockedSection LockedSection(Lock); ... } }; */ class CLock { HANDLE Mutex; // used to lock/unlock object public: CLock() : Mutex(::CreateMutex(0,0,0)) {} virtual ~CLock() {try{::CloseHandle(Mutex);}catch(...){ASSERT(0);}} bool Lockable () const {return Mutex!=0;} bool Lock() { while(Mutex) { if(MsgWaitForMultipleObjects(1, &Mutex, FALSE, INFINITE, QS_ALLINPUT)==WAIT_OBJECT_0) return true; for(MSG Msg; PeekMessage(&Msg, 0,0,0, PM_REMOVE); DispatchMessage(&Msg)) TranslateMessage(&Msg); // This Message Pump just allows the Thread to maintain any Windows it may have } return false; // The Mutex creation failed. } void Unlock() {if(Lockable()) ReleaseMutex(Mutex);} }; class CLockedSection { CLock& Lock; public: CLockedSection(CLock& Lock) : Lock(Lock) {Lock.Lock();} virtual ~CLockedSection() {try{Lock.Unlock();}catch(...){ASSERT(0);}} }; #endif // Threadh