Events and tasks

Win32 or MFC application displays the main window and then the application listens to user events. Users may select a menu item or click on a button or click on the toolbar button. These are the actions by the user and there will trigger an event for the action. Now application defines a job or task to perform upon receiving the event. Now, these tasks may be very small and take few CPU cycles to complete or maybe long and heavy enough. Long and heavy tasks can not be handled inside the event handler. These long tasks take CPU cycles and it will block the main message processing thread which will result in an unresponsive GUI.

Worker thread

A worker thread is a small task or thread to handle the background work of an application. The worker thread starts the execution on a new CPU context and thus does not block the main event queue. So the UI thread creates a worker thread and the UI event returns. The message queue processing continues within the main thread and there is no further blocking in UI processing. Here are some practical examples of worker threads in different applications.

Worker thread in 7z

7z is a file compression tool used widely for archiving and it uses a worker thread to perform the task of compress and decompress of files. The worker thread updates the status and progress of the work using a progress bar dialog box.

Worker thread in 7z tool and progress bar dialog

Worker thread in Windows explorer

Windows explorer is an operating system built-in application used in managing files and folders. We see a dialog box with progress bar status while performing copy, delete and move operations of files and folders.

Worker thread in Windows Explorer and progress bar dialog

Worker thread in MFC application

We can display this dialog box with a simple progress bar to show the progress of a long-running task. We can update some status text and the progress percentage from the worker thread. We have an example to show this mechanism.

Worker thread and MFC progress bar dialog

AfxBeginThread & Worker threads

AfxBeginThread function is used in MFC to create threads in an MFC application. AfxBeginThread is a polymorphic function and it takes different arguments for creating UI thread and worker thread. An MFC worker thread can be created with the below function prototype. It takes a callback function which is also known as thread routine and the context parameter as the mandatory arguments for scheduling a worker thread.

/*Creates Worker thread*/
CWinThread* AFXAPI AfxBeginThread (
  AFX_THREADPROC pfnThreadProc,
  LPVOID pParam,
  int nPriority = THREAD_PRIORITY_NORMAL,
  UINT nStackSize = 0,
  DWORD dwCreateFlags = 0,
  LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);

Calling AfxBeginThread

  • pfnThreadProc - Programmer should provide a valid address of the worker thread procedure and should not be NULL. This prototype of the thread function must be as follows: UINT ThreadProc( LPVOID pParam );
  • pParam - Context parameter of the thread. This pointer is passed to the controlling function whose address is pointed by pfnThreadProc.
  • nPriority (optional)- The desired priority value of the worker thread. Zero indicates the same priority as the creating thread. The value and meaning of the priority can be found in the Win32 Programmer’s Reference in SetThreadPriority API.
  • nStackSize (optional)- This parameter tells the kernel to construct the stack of the thread with the size in bytes. Zero indicates the same size stack as the creating thread.
  • dwCreateFlags (optional)- An additional flag that controls the creation of the worker thread. There are two possible value of this flag :
    • Zero (0) - Execute the thread immediately after creation.
    • CREATE_SUSPENDED - The thread object will be created with a suspend state. ResumeThread should be called to start the execution of the thread routine.
  • lpSecurityAttrs (optional)- This should point to a SECURITY_ATTRIBUTES structure that specifies the security attributes for the thread. The NULL value should be used to use the same security attributes as the creating thread.

Create Worker thread with AfxBeginThread

This is a small dialog-based MFC application. It has a button and OnWorker() is the event handler for the button. We are creating a long-running thread or worker thread in the event using AfxBeginThread function. WorkerThread is the worker thread routine and it simply displays a time and waits using sleep. The thread creates with the context of the dialog window handle so the thread can update the dialog text using SetWindowText() call. The dialog has a member m_pThread to save the thread object pointer and once the user clicks the button it sets m_pThread = NULL and the thread returns with zero and deletes from memory using AfxEndThread call.

#include <afxwin.h>

#define ID_WORKER 1001

class CMyWnd : public CFrameWnd
{
private :
  CButton m_BWorker;
  CWinThread *m_pThread;
  bool m_bThread;
  friend  UINT WorkerThread(LPVOID arg);

public :
  CMyWnd()
  {
    CRect rt;
    rt.top = 10;
    rt.left = 150;
    rt.right = 250;
    rt.bottom = 30;
    Create (NULL, _T("MFC Worker Thread"));
    m_BWorker.Create ("Worker",
                     WS_CHILD|WS_VISIBLE,
                     rt,
                     this,
                     ID_WORKER);
    m_pThread = NULL;
    m_bThread = TRUE;
  }
  afx_msg void OnWorker();

  DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CMyWnd, CFrameWnd)
ON_COMMAND(ID_WORKER, OnWorker)
END_MESSAGE_MAP()

UINT WorkerThread(LPVOID arg)
{
   SYSTEMTIME st;
   CMyWnd * pWnd;
   CMyWnd *pWnd = (CMyWnd *)arg;
   while (pWnd->m_bThread) {
     GetSystemTime(&st);
     CString tm;
     tm.Format("Time %.2d:%.2d:%.2d Date %.2d/%.2d/%.4d",
             st.wHour, st.wMinute, st.wSecond, st.wDay, st.wMonth, st.wYear);
     pWnd->SetWindowText((LPCTSTR)tm);
     Sleep(500);
   }
   AfxEndThread(0, TRUE);
   return 0;
}
afx_msg void CMyWnd::OnWorker()
{
  if(!m_pThread) {
    m_bThread = TRUE;
    m_pThread = AfxBeginThread (WorkerThread,
                                this,
                                THREAD_PRIORITY_NORMAL,
                                0,
                                0,
                                NULL);
  } else {
    m_bThread = FALSE;
    m_pThread = NULL;
  }
}

class CMyApp : public  CWinApp 
{

public:
  virtual BOOL InitInstance()
  {
    m_pMainWnd = new CMyWnd();
    m_pMainWnd->ShowWindow(SW_SHOW);
    m_pMainWnd->UpdateWindow();
    return TRUE;
  }
};
CMyApp app;

MFC Worker Thread Demo

About our authors: Team EQA

Further readings

Where is WinMain() function in MFC application ?

MFC hides WinMain in its framework and includes source file on WinMain(). This explains how framework calls global CWinApp::Initinstance() from entry WinMain.

What is the utility of CWinApp class?

This is constructed during global C++ objects are constructed and is already available when Windows calls the WinMain function, which is supplied by the ...

Basic steps in Win32 GUI Application with source code.

Define a custom Window class structure, Register the class name, CreateWindow, Show windows and write message get and dispatch loop statements. Define the Window CallBack procedure and write the handlers.

What is a Window CallBack procedure and what is its utility?

DispatchMessage() is a API which indirectly triggers the Window CallBack procedure. Message structure members from this function are passed to the CallBack procedure. CallBack procedure should implement event handlers depending on the need of the application.

What are LPARAM and WPARAM in window proc function?

LPARAM and WPARAM are the two parameters in Window CallBack procedure. They signifies parameters of various events. They are used in handing individual events.

What are the basic steps of a typical MFC based application?

We need to write WinMain and need to follow all these in a Win32 application. However we need not to write much if we are writing an application with MFC ...

#