Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Init Threads in MFC and Win32

5.00/5 (19 votes)
17 Oct 2021CPOL5 min read 22.6K   1.8K  
Starting Threads in MFC and Win32 and some handling samples
In this article, you will see a project sample for people like me who have just started to learn about multithreading.

MFC:

Win32:

 

InitThreadsTitle

Introduction

In some of my project development, the multithreading technologies turned out to be useful. And as usual, I looked at CodePoject and found the fine article Synchronization in Multithreaded Applications with MFC by Arman S. The idea and sub-script codes are good. It is a good starting point for multithreading habituation.

Just out of the routine, the demo implementation occurred to not be as good. It is a poor idea of drawing white rectangle in the old ellipse position before drawing the new ellipse. White space occurred visible while the ellipses overlapping.

Therefore, I've taken the liberty of improving the performance by means of the standard MSVS AppWizard procedures and added up some handling features.

Background

The article above was written in 2007, thus the project WorkerThreads provided is not directly compatible with the latest versions of MSVS. Therefore, I've created a new single documented project named WorkerThreads with MSVS-2012 pro, replaced codes of WorkerThreadsView.h and WorkerThreadsView.cpp with the same of the Arman S. project and included Thread.h and Thread.cpp from the same sources. The executable file compiled produced the video performance as follows:

workerthreads

In order to improve the performance above, I developed a new project InitThreads. The executable InitThreads.exe has been built with MSVS-2012 pro.

Demo Explanations

Before you start building the project provided, it is highly recommended to have a look at the demo presentation enclosed in order to get an idea of the output expected.

Some menu and some special Accelerator keys arranged in order to demonstrate the InitThreads project implementation:

InitThreadsInfo

  • File->New Ctrl + N - New Ball in random position with random velocity and random colour
  • File->Delete Ctrl + D - Delete the Balls selected
  • File->Reset Ctrl + R - Delete All the Balls
  • File->Exit Alt + F4 - Exit Application
  • Edit->Unselect All Ctrl + U - Unselect All the Balls selected if any
  • Edit->Free Balls Ctrl + F - Balls moving transparently through each other
  • Edit->Glancing Balls Ctrl + G -Balls are solid and not overlapping
  • Edit->Bumping Balls Ctrl +B -Balls bumping elastic
  • View->XOR Ctrl + X - Set/Reset XOR Overlapping
  • View->Tool Bar - Show/Hide Tool Bar
  • View->Status Bar - Show/Hide Status Bar
  • Help->Help F1 - Show Help Dialog box
  • Help->About InitThreads - Show About Dialog box
  • Mouse Left Button Down - Select/Unselect Ball if Touched; Otherwise-New Ball in Cursor position with random velocity and random colour

All the commands above are also available on Help Dialog Box, just press F1 button:

Help Dialog Box

The Help Dialog Box is non-modal, therefore you may select any command in the list control and press OK button or double click Mouse Left Button.

In the About Dialog Box, the reference to the original article by Arman S. is presented:

About Dialog Box

Building Notes.

Solution configuration must be installed as Release and the platform to be Win32.

The project provided has been developed with MSVS-2015 pro using the instruments of MSVS-2010. Therefore, the exe files are valid even for Windows-XP. If you do not need to run the application in Windows-XP, just change the instruments to MSVS-2015.

The default coding property is UNICODE; nevertheless MBCS coding is also available, just change the property.

Even if you are working for the first time with MSVS, just select menu Debug->Start without debugging and the program InitThreads.exe should start building and working.

Project Source and Data Storage

Standard source codes in InitThreadsproj path have been created with the standard MFC Application Wizard:

  • InitThreads.cpp, InitThreadsDoc.cpp - Defines the standard class behaviors for the application
  • InitThreadViewcpp - Implementation of the standard CView class; messages handling procedures created by the author using standard MFC Application Wizard procedures
  • BallThread.cpp - Derived from the standard MFC CWinThread class, overwritten by the author
  • InitThreads.rc and resource.h - Menu, dialog, accelerator resources created by the author using the standard Resource Wizard procedures

Code Explanation

All the Menu and Accelerator Commands have been done with standard MFC AppWizard technologies. Therefore, I feel nothing to explain better than in the standard tutorials. Key moments in solution:

  1. Deriving new CBallThread class from the standard MFC CWinThread class:

    C++
     class CBallThread : public CWinThread
    {
    	DECLARE_DYNCREATE(CBallThread)
    protected:
    	CBallThread();                    // protected constructor used by dynamic creation
    public:
    	virtual ~CBallThread();  
    public:
    	virtual BOOL InitInstance();
    	virtual int ExitInstance();
    	CPoint point;                     //initial point were ball born
    	CInitThreadsView * m_pView;       //reference to the main View
    	float x,y;                        //Position of the center of the ball 
                                          //from the top left corner
            float vx, vy;                 //Velocity ( number of pixels passed in one step)
    	int m_nCount;                     //Item No
    	BOOL m_bSelected;                 //Selection flag
    	COLORREF m_color;                 //Colour of the ball			
            void InitBall(CPoint point);  //Ball initialization in the point designated
    	UINT ThreadStep(void);            //One step moving
    	void DrawMyBall(CDC * pDC);	      //Draw Ball into graphics context designated	
    	int Intersection( CBallThread * pB);   //Intersection with another ball
    protected:
    	DECLARE_MESSAGE_MAP()
    public:
    	virtual BOOL OnIdle(LONG lCount);
    }; 
  2. Starting the new CBallThread in the point designated (randomly if menu File->New command used or the cursor position if Mouse Left Button pressed):

    C++
     void CInitThreadsView::StartBallThread(CPoint point)
    {
    	//New Thread Created:
        CBallThread *pThread = (CBallThread *)AfxBeginThread(RUNTIME_CLASS(CBallThread),  
                                THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
    	pThread->point = point;          //designated point
    	pThread->m_pView = this;         //Reference to the main View
        pThread->m_bAutoDelete = FALSE;  //Specifies whether the CWinThread object should be 
                                         //automatically deleted at thread termination.
        pThread->ResumeThread();         //New Thread Started
        m_BallList.AddTail(pThread);     //Append the New Thread tob the object list
    } 
  3. After the initialization, every thread moving by the steps in her own life:

    C++
    UINT CBallThread::ThreadStep(void)
    {
    	InitBall(point);
    	while (WAIT_TIMEOUT == ::WaitForSingleObject(g_eventEnd, 0))// Ball's life loop
    	{
    		CRect rectClient;
    		m_pView->GetClientRect(rectClient);           //Rectangle of the main View
    		//define the Rectangle containing ellipse:
            CRect rectEllipse(Round(x) - m_pView->r, Round(y) - m_pView->r, 
                                         Round(x) + m_pView->r, Round(y) + m_pView->r);
    		int xt = rectEllipse.left - rectClient.left;  //is the ellipse outside 
                                                          //from the left
    		if (xt <=0 ){vx *= -1; x -= xt;}
    		else
    		{ xt = rectClient.right - rectEllipse.right;  //is the ellipse outside 
                                                          //from the right
    		if (xt <=0 ){vx *= -1;x += xt; }
    		}
    		int yt = rectEllipse.top - rectClient.top;    //is the ellipse outside 
                                                          //from the top
    		if (yt <=0 ){vy *= -1; y -= yt;}
    		else
    		{yt = rectClient.bottom - rectEllipse.bottom; //is the ellipse outside 
                                                          //from the bottom
    		if (yt <=0 ){vy *= -1; y += yt;}
    		}
    
             POSITION psn = m_pView->m_BallList.Find(this);
             if(psn == NULL){ExitInstance(); Delete();}  //If the ball is not in list, 
                                                         //it must be dead
               else
            if(m_pView->m_bBumpingBalls || m_pView->m_bSolidBalls )//if the balls see 
                                                                   //each other
            {m_pView->m_BallList.GetNext(psn);
             for(POSITION pos = psn; pos != NULL;)       //check the intersection with 
                                                         //all the balls after  
                                                         //current in list
    	     {
    		    CBallThread * ptr = (CBallThread *)m_pView->m_BallList.GetNext(pos);
    		    Intersection(ptr);
    	      }
            }
             x+=vx;  y+=vy;                              //move into one step
    		Sleep(m_pView->m_nTick);                     //wait for time of the main timer
         }
    	return 0;
    }
  4. The complete scene of all the balls performed in m_nTick milliseconds interval in the main View:

    C++
        void CInitThreadsView::DrawScene(void)
    {
    	CRect rect;                 //main View rectangle
    	GetClientRect(&rect);   
    	//Clear memory graphics context:
    	dcMem.BitBlt(0, 0, rect.Width(), rect.Height(), NULL, 0, 0, WHITENESS);
    	//Install ROP2 overlapping mode designated:
    	int R2mode = m_bXOR ? dcMem.SetROP2(R2_NOTXORPEN) : dcMem.GetROP2();
    	for(POSITION pos = m_BallList.GetHeadPosition(); pos != NULL;)
    	{
    		CBallThread * ptr = (CBallThread *)m_BallList.GetNext(pos);  
    		ptr->DrawMyBall(&dcMem);
    	}
         dcMem.SetROP2(R2mode);
    	 //copy memory graphics context to the main View:
    	GetDC()->BitBlt(0, 0, rect.Width(), rect.Height(), &dcMem, 0, 0, SRCCOPY);
    }    

As for inner threads behavior, I think better to refer to the article by Arman S.

Your Own Applications Development using the Project Provided

You may pick up all this project, rename it with the project of my former CodeProject article "MFC Project from Existing Code in One Click" and combine and improve the codes as you like.

Your references to my codes, if any, should be highly appreciated.

Points of Interest and Acknowledgements

If you change the status of the balls intersection, you can observe different pictures of balls moving. Thus glancing blows look as follows:

InitThreadsClance

From some comments to the first publicaion of this I discovered that some people are not happy with MFC performance. Especially for those people  angry with MFC I developed Win32 project of the same with the same functions. In the sample below in Win32 version the balls allowed to overlap and picked out a speed as bumping blows:

Win32Threads

It is worth nothing that this article and codes are not about graphics and balls' bumping. The idea is of creating the thread objects living there own life parallel to the main program.

I believe this project will be interesting for people who are just starting to study multithreading technologies.

Many thanks to Arman S. for his fine job. Also, my granddaughter likes to play with colour bubbles.

History

  • 7th October, 2021: Initial version
  • 17th October, 2021:Win32 project added and some corrections to MFC project done

 

License

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