Click here to Skip to main content
15,867,453 members
Articles / Desktop Programming / MFC

Hello, I Am Ready to Communicate!

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
20 Jun 2012CPOL4 min read 27K   366   14   7
CWinThread with TWO-Way communication using window message

Introduction

I was working on one of my pet projects yesterday night, there was a requirement of using UI thread. As I had the liberty of using MFC, I thought of using CWinThread. When I dived more into its concepts , I though of writing an article on this highly magestic OLD technology, far away from the .NET world.

Using the Code

So now, here is our problem statement.

Problem Statement #1

"Create a UI Thread, which posts message to Main thread every second increasing the counter by 1."

Step By Step Guide to Solve Problem #1

  1. These days, VS wizards are so powerful, you don’t need to write MFC Classes derived classes by hand, you just need to right click on project in Class View and Add Class derived from CWinThread. So just right Click on Project Name ->Add->Class
  2. After the dialog box is open, just name new class as CCountingThread. Following is the skeleton that would be produced by Wizard:
    C++
    class CCountingThread : public CWinThread
    {
        DECLARE_DYNCREATE(CCountingThread)
    protected:
        CCountingThread();           // protected constructor used by dynamic creation
        virtual ~CCountingThread();
     
    public:
        virtual BOOL InitInstance();
        virtual int ExitInstance();
    protected:
        DECLARE_MESSAGE_MAP()
    };
  3. Now add three variables to the class to serve our purpose:
    • UINT_PTR m_uTimerID: will keep track of SetTimer ID
    • int m_iCount: will keep track of next incremented value
    • CWnd* m_pParentWnd: will keep pointer to the main window. I know there is one more variable in CWinThread itself to this task (m_pMainWnd). However, I just want to keep it simple.
  4. Now since the Main Dialog must be updated every second with a new value, I thought of using WM_TIMER message with elapsed time of 1 second and on every timer message, post message to main window with new value. So now, add new function in CCountingThread to handle WM_TIMER message.
    C++
    void CCountingThread::OnTimer(WPARAM wParam, LPARAM lParam)
    {
        m_pParentWnd->PostMessageW(WM_USER+2,0,++m_iCount); ---- (1)
    }

    Here, I have written code for posting message to main window with new value in (1).

  5. Now to make our CCountingThread::OnTimer (…) function visible to MessageLoop for WM_TIMER message, add the following code between BEGIN_MESSAGE_MAP() and END_MESSAGE_MAP().
    C++
    ON_THREAD_MESSAGE(WM_TIMER,&CCountingThread::OnTimer)
  6. Now, write the activation code for timer in CCountingThread::InitInstance() and de-activation code in CCountingThread::ExitInstance() as follows:
    C++
    BOOL CCountingThread::InitInstance(){
        m_uTimerID = SetTimer(NULL,2001,1000,NULL);
        return TRUE;
    }
    int CCountingThread::ExitInstance(){
        KillTimer(NULL,m_uTimerID);
        return CWinThread::ExitInstance();
    }

    After the above task for our WinThread derived class is complete.

  7. Now, design main window UI with One Edit Box (for displaying value from Thread) and two buttons (For Starting and Stopping Thread) and add relevant handler and control variable for edit box. Also add pointer to CCountingThread in class, before doing previous don’t forget to add CountingThread.h in your mainwindow class header file.
  8. Add the following code to your Start button handler:
    C#
    void CUserThread1Dlg::OnBnClickedStartThread()
    {
        if(m_pRunningThread== NULL)
        {
            m_pRunningThread = (CCountingThread*)AfxBeginThread(
                RUNTIME_CLASS(CCountingThread),
                0,
                0,
                CREATE_SUSPENDED,
                NULL); --- (a)
     
            m_pRunningThread->m_pParentWnd = this; -- (b)
            m_pRunningThread->ResumeThread();  --- (c) 
        }
    }
    1. Create our UI thread in suspended mode using AfxBeginThread API, pass runtime class of CCountingThread as the first parameter and CREATE_SUSPENDED as the fourth parameter.
    2. Provide m_pRunningThread->m_pParentWnd, our main window pointer for communication
    3. m_pRunningThread->ResumeThread(): will start our thread
  9. Now write the following code for stopping our UI Thread:
    C++
    void CUserThread1Dlg::OnBnClickedStopThread()
    {
    	if(m_pRunningThread!= NULL)
    	{
    		m_pRunningThread->PostThreadMessageW(WM_QUIT,0,0); --- (a)
    
    		m_pRunningThread = NULL; --(b)
    	}
    }
    1. The best way to close UI thread is to post WM_QUIT message to the thread, it would close down gracefully. Since WM_QUIT makes message-pump of UI thread to exit.
    2. I am making m_pRunningThread equal to NULL, this is ok with DEMO scenario, however in real world problem, you need to program for synchronization and proper exit of thread, then assign m_pRunningThread the value NULL.
  10. Our UI thread is posting WM_USER+2 messages every second with updated counter value, so add function in our dialog class to handle it. So add the following code:
    C++
    LRESULT CUserThread1Dlg::OnCountingIncrease(WPARAM wParam, LPARAM lParam)
    {
    	CString strText;
    	strText.Format(_T("%d"),lParam);
    	m_edtCounting.SetWindowTextW(strText);
    	return LRESULT(0);
    }

    And add message listener in BEGIN_MESSAGE_MAP():

    C++
    ON_MESSAGE(WM_USER+2, &CUserThread1Dlg::OnCountingIncrease)
  11. Build and run your application to see it work.

… Wait a minute, I told you there would be two way communication, however here it’s just one way, means UI thread is sending the message and Main window is listening. So implement this, i.e., Two Way communication, let's extend our problem statement #1 and derive the following new statement:

Problem Statement #2

“To add, reset counter button to reset count back to Zero”.

Step by Step Guide

  1. In main dialog box, add new button “Reset Counter” and add handler in your code.
  2. Now, add the following code into the OnClick handler to “Reset Counter” button:
    C++
    void CUserThread1Dlg::OnBnClickedResetThreadcounter()
    {
    	if(m_pRunningThread!= NULL) –(a)
    	{
    		m_pRunningThread->PostThreadMessageW(WM_USER+1,0,0); -- (b)
    	}
    }
    1. m_pRunningThread is object of class CCountingThread, created at the time of Start Thread button click.
    2. using PostThreadMessageW we will post message to UI thread.
  3. Now, add function in class CCountingThread to handle WM_USER+1 user message sent by Main Dialog box:
    C++
    void CCountingThread::ResetCounter(WPARAM wParam, LPARAM lParam){
    	m_iCount =0; -- reset the counter
    }
  4. Also add the following message listener in BEGIN_MESSAGE_MAP() and END_MESSAGE_MAP():
    C++
    ON_THREAD_MESSAGE(WM_USER+1,&CCountingThread::ResetCounter)
  5. Compile and run the application.

History

  • 20-Jun-2012: First version

License

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


Written By
Software Developer (Senior)
India India
He used to have biography here Smile | :) , but now he will hire someone (for free offcourse Big Grin | :-D ), Who writes his biography on his behalf Smile | :)

He is Great Fan of Mr. Johan Rosengren (his idol),Lim Bio Liong, Nishant S and DavidCrow and Believes that, he will EXCEL in his life by following there steps!!!

He started with Visual C++ then moved to C# then he become language agnostic, you give him task,tell him the language or platform, he we start immediately, if he knows the language otherwise he quickly learn it and start contributing productively

Last but not the least, For good 8 years he was Visual CPP MSMVP!

Comments and Discussions

 
QuestionWhat exactly is the purpose... Pin
waleri20-Jun-12 20:12
waleri20-Jun-12 20:12 
AnswerRe: What exactly is the purpose... Pin
ThatsAlok20-Jun-12 21:36
ThatsAlok20-Jun-12 21:36 
AnswerRe: What exactly is the purpose... Pin
john morrison leon26-Apr-15 23:37
john morrison leon26-Apr-15 23:37 
It isn't hard to work out that the purpose is to allow two threads to communicate with each other without blocking.

The solution is very straightforward (some would say trivial) but nevertheless many people miss it and employ much more complicated mechanisms to do the same thing.
It is helpful to have articles that highlight often missed simple solutions.

What the article lacks is a narrative that says this.
AnswerRe: What exactly is the purpose... Pin
ThatsAlok12-May-15 0:47
ThatsAlok12-May-15 0:47 
QuestionTitle Pin
Nasir Razzaq20-Jun-12 4:15
Nasir Razzaq20-Jun-12 4:15 
AnswerRe: Title Pin
ThatsAlok20-Jun-12 19:32
ThatsAlok20-Jun-12 19:32 
AnswerRe: Title Pin
ThatsAlok20-Jun-12 19:32
ThatsAlok20-Jun-12 19:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.