Figure 3    CCancelDlg

CancelDlg.h


 ////////////////////////////////////////////////////////////////
 // 1998 Microsoft Systems Journal. 
 // If this code works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 // 
 
 ////////////////
 // Generic Cancel Dialog. To use it, you must design a dialog with
 // an IDCANCEL button, then instantiate CCancelDlg using your ID as the
 // resource, and call CDialog::Create to create a modeless dialog. Then
 // start your "long process". You must call CCancelDlg::Abort periodically
 // to see if the user has aborted, and if so, quit.
 //
 class CCancelDlg : public CDialog {
 public:
     CCancelDlg() : m_bAbort(FALSE) { }
     void Reset() { m_bAbort = FALSE; }
     virtual BOOL Abort();
 
 protected:
     BOOL m_bAbort;
     virtual void OnCancel();
 };
CancelDlg.cpp

 ////////////////////////////////////////////////////////////////
 // 1998 Microsoft Systems Journal. 
 // If this code works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 // Compiles with Visual C++ 5.0 on Windows 95
 //
 // Implementation for CCancelDlg, a generic cancel dialog.
 // 
 #include "stdafx.h"
 #include "CancelDlg.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 //////////////////
 // User pressed Cancel: set flag
 //
 void CCancelDlg::OnCancel()
 {
     m_bAbort = TRUE;
 }
 
 //////////////////
 // Test for abort. This is my chance to run peek/pump message loop;
 // ie, to process any messages that may be waiting for me
 // or--in Windows 3.1--for other apps as well.
 //
 BOOL CCancelDlg::Abort()
 {
     MSG msg;
     while (::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE)) {
            AfxGetThread()->PumpMessage();
     }
     return m_bAbort;
 }
 

Figure 4    CANCEL1


////////////////////////////////////////////////////////////////
// 1998 Microsoft Systems Journal. 
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 5.0 on Windows 95
//
// Shows how to use CCancelDlg.
// 
#include "stdafx.h"
#include "resource.h"        // main symbols
#include "CancelDlg.h"

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

// This function simulates the long operation of dumping a record.
//
const NUMRECS = 100;
void DumpRecord(int n)
{
    Sleep(500); // sleep half a second
}

//////////////////
// My cancel dialog: derived from generic cancel dialog to report status too.
//
class CMyCancelDlg : public CCancelDlg {
public:
    CString m_sProgress;
    virtual void DoDataExchange(CDataExchange* pDX);
};

////////////////
// Data exchange: use static text control to display # records dumped
// Standard MFC DDX stuff.
//
void CMyCancelDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Text(pDX, IDC_PROGRESS, m_sProgress);
}

//////////////////
// Application main window is a dialog with "Begin Dumping"
// and "Exit" buttons.
//
class CMainDlg : public CDialog {
public:
    CMainDlg() : CDialog(IDD_MAINFRAME) { };
protected:
    afx_msg void OnBeginDump();
    DECLARE_MESSAGE_MAP()
};

BEGIN_MESSAGE_MAP(CMainDlg, CDialog)
    ON_COMMAND(IDC_BEGIN_DUMP, OnBeginDump)
END_MESSAGE_MAP()

//////////////////
// User clicked "Begin Dumping" button: start dumping.
//
void CMainDlg::OnBeginDump() 
{
    EnableWindow(FALSE);               // disable myself
    CMyCancelDlg dlg;                  // create cancel dialog object..
    dlg.Create(IDD_CANCELDLG, this);   // ..and window (modeless dialog)

    // Now dump the records. Quit if CCancelDlg::Abort returns TRUE.
    for (int i=0; i<NUMRECS && !dlg.Abort(); i++) {
        ::DumpRecord(i);
        dlg.m_sProgress.Format(_T("Dumping record %d of %d"),
            i+1, NUMRECS);
        dlg.UpdateData(FALSE);
    }

    // done, or user canceled
    dlg.DestroyWindow();               // destroy cancel dialog
    EnableWindow(TRUE);                // enable myself..
    SetForegroundWindow();             // ..and make foreground window
}

//////////////////
// Application class
//
class CMyApp : public CWinApp {
public:
    virtual BOOL InitInstance();
};

CMyApp theApp;

//////////////////
// MFC InitInstance: run main dialog and then quit.
//
BOOL CMyApp::InitInstance()
{
    CMainDlg dlg;         // create main dialog
    m_pMainWnd = &dlg;    // set main window for good luck
    dlg.DoModal();        // run the dialog..
    return FALSE;         // ..and then quit
}

Figure 5   CthreadJob

ThreadJob.h


 ////////////////////////////////////////////////////////////////
 // 1998 Microsoft Systems Journal. 
 // If this code works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 // 
 
 ////////////////
 // Generic worker thread object. Use it to run a task in a separate thread.
 // To use:
 //  * Derive your own class from CThreadJob and implement DoWork:
 //     - periodically check m_bAbort
 //     - call OnProgress if you want to report progress
 //  * Create an instance of your class (not on stack!)
 //  * Call Begin to start, with CWnd and msg ID for OnProgress notifications.
 //  * Call Kill to abort
 //
 class CThreadJob : public CObject {
 private:
     static UINT ThreadProc(LPVOID pObj);
     CWinThread* m_pThread;  // running thread, if any
 
 protected:
     HWND    m_hWndOwner;    // HWND, *not* CWnd* of owner window
     UINT    m_ucbMsg;       // callback message for OnProgress
     UINT    m_uErr;         // thread error code
     BOOL    m_bAbort;       // whether to abort: DoWork must check this
 
     // Call this from DoWork to report progress.
     // Meaning of WPARAM/LPARAM is up to you.
     void OnProgress(WPARAM wp=0, LPARAM lp=0);
 
     // You must implement to do the work
     virtual UINT DoWork() = 0;
 
 public:
     virtual BOOL Begin(CWnd* pWndOwner=NULL, UINT ucbMsg=0);
     virtual void Kill();
     DECLARE_DYNAMIC(CThreadJob)
 };

ThreadJob.cpp


 ////////////////////////////////////////////////////////////////
 // 1998 Microsoft Systems Journal. 
 // If this code works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 // Compiles with Visual C++ 5.0 on Windows 95
 //
 // Implementation for CThreadJob, a generic worker thread.
 // 
 #include "stdafx.h"
 #include "ThreadJob.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 IMPLEMENT_DYNAMIC(CThreadJob, CObject)
 
 //////////////////
 // Thread proc calls virtual DoWork function. This converts the
 // Windows/C-style thread procedure into an MFC/C++-style virtual function.
 // To do the "work" of the thread, implement DoWork and don't worry about
 // the thread proc.
 //
 UINT CThreadJob::ThreadProc(LPVOID pObj)
 {
     CThreadJob* pJob = (CThreadJob*)pObj;
     ASSERT_KINDOF(CThreadJob, pJob);
     pJob->m_uErr = pJob->DoWork();  // call virt fn to do the work
     pJob->m_pThread = NULL;         // done: clear
     return pJob->m_uErr;            // ..and return error code to Windows
 }
 
 //////////////////
 // Begin running the worker thread. Args are owner window and callback
 // message ID to use for OnProgress notifications, if any. You could enhance
 // this to expose priority and other AfxBeginThread args.
 //
 BOOL CThreadJob::Begin(CWnd* pWndOwner, UINT ucbMsg)
 {
     m_hWndOwner = pWndOwner->GetSafeHwnd();
     m_ucbMsg = ucbMsg;
     m_bAbort = FALSE;
     m_uErr = 0;
     m_pThread = AfxBeginThread(ThreadProc, this);
     return m_pThread != NULL;
 }
 
 //////////////////
 // Abort the thread. All this does is set m_bAbort = TRUE.
 // It's up to you to check this flag periodically in your DoWork function.
 //
 void CThreadJob::Kill()
 {
     m_bAbort = TRUE;
 }
 
 //////////////////
 // Report progress generically in the form of WPARAM/LPARAM.
 // Your DoWork function can call this whenever it likes to post a message
 // to the owning window. It's up to you what wp/lp mean.
 // OnProgress uses PostMessage (instead of SendMessage) so the code that
 // handles the thread will run in the owner window's thread.
 //
 void CThreadJob::OnProgress(WPARAM wp, LPARAM lp)
 {
     if (m_hWndOwner && m_ucbMsg)
         ::PostMessage(m_hWndOwner, m_ucbMsg, wp, lp);
 }

Figure 6   CANCEL2


 ////////////////////////////////////////////////////////////////
 // 1998 Microsoft Systems Journal. 
 // If this code works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 // Compiles with Visual C++ 5.0 on Windows 95
 // Shows how to use CThreadJob
 #include "stdafx.h"
 #include "resource.h"           // main symbols
 #include "ThreadJob.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 //////////////////
 // Message used for progress notification
 //
 const UINT WM_MYPROGRESS = WM_USER;
 
 // This function simulates the long operation of dumping a record.
 //
 const NUMRECS = 100;
 void DumpRecord(int n)
 {
     Sleep(500); // sleep half a second
 }
 
 //////////////////
 // Dump record job is a special case of CThreadJob, a worker thread
 //
 class CDumpRecordsJob : public CThreadJob {
 public:
     virtual UINT DoWork();
 };
 
 //////////////////
 // This is the function that does the work of the job
 //
 UINT CDumpRecordsJob::DoWork()
 {
     // Dump the records. Quit if m_bAbort is TRUE.
     for (int i=0; i<NUMRECS && !m_bAbort; i++) {
            ::DumpRecord(i);        // dump next record
                 OnProgress(i+1);   // report progress
     }
     OnProgress(m_bAbort ? -1 : 0); // report done or cancelled
     return 0;
 }
 
 //////////////////
 // Application main window is a dialog with "Begin Dumping", "Stop",
 // and "Exit" buttons, and a static text control to show progress.
 //
 class CMainDlg : public CDialog {
 public:
     CMainDlg() : CDialog(IDD_MAINFRAME) { };
 protected:
     CStatic    m_wndProgress;     // static text for progress
     CButton    m_wndBeginDump;    // "Begin Dumping" button
     CDumpRecordsJob m_job;        // worker job (thread)
 
     virtual BOOL OnInitDialog();
     afx_msg void OnBeginDump();
     afx_msg void OnStop();
     afx_msg void OnProgress(WPARAM wp, LPARAM lp);
     DECLARE_MESSAGE_MAP()
 };
 
 BEGIN_MESSAGE_MAP(CMainDlg, CDialog)
     ON_COMMAND(IDC_BEGIN_DUMP, OnBeginDump)
     ON_COMMAND(IDC_STOP_DUMP,  OnStop)
     ON_MESSAGE(WM_MYPROGRESS,  OnProgress)
 END_MESSAGE_MAP()
 
 //////////////////
 // Initialize dialog: subclass controls
 //
 BOOL CMainDlg::OnInitDialog()
 {
     m_wndProgress.SubclassDlgItem(IDC_PROGRESS, this);
     m_wndBeginDump.SubclassDlgItem(IDC_BEGIN_DUMP, this);
     return TRUE;
 }
 
 //////////////////
 // User clicked "Begin Dumping" button: start job (thread)
 //
 void CMainDlg::OnBeginDump() 
 {
      m_wndBeginDump.EnableWindow(FALSE);    // disable start button
      m_job.Begin(this, WM_MYPROGRESS);      // and start the job
 }
 
 //////////////////
 // User clicked "Stop" button: kill the job (thread)
 // 
 void CMainDlg::OnStop() 
 {
     m_job.Kill();
 }
 
 //////////////////
 // Handle progress notification from thread.
 //    wp = 0   ==> done
 //    wp = -1  ==> aborted
 //    else wp  =   number of records dumped.
 // 
 void CMainDlg::OnProgress(WPARAM wp, LPARAM lp)
 {
     CString s;
     if (wp==0)
     s = _T("Done");
     else if (wp==-1)
     s = "Aborted";
     else
     s.Format(_T("Dumping record %d of %d"), wp, NUMRECS);
 
     if (wp<=0)
     // abort or done: re-enable Start button
     m_wndBeginDump.EnableWindow(TRUE);
 
     m_wndProgress.SetWindowText(s);
 }
 
 //////////////////
 // Application class
 //
 class CMyApp : public CWinApp {
 public:
     virtual BOOL InitInstance();
 };
 
 CMyApp theApp;
 
 //////////////////
 // MFC InitInstance: run main dialog and then quit.
 //
 BOOL CMyApp::InitInstance()
 {
     CMainDlg dlg;       // create main dialog
     m_pMainWnd = &dlg;  // set main window for good luck
     dlg.DoModal();      // run the dialog..
     return FALSE;       // ..and then quit
 }