C++ sendng messages to GUI controls from different threads (thread - safety)

Posted on 2011-04-26
Last Modified: 2012-05-11
Hi Experts,
I would like to set my mind straight on this once and for all.  When calling GUI controls from other threads, I know you have to send messages.  Can thIs be the case for non-GUI objects?

As an example to help answer this question, I'm writing a progress bar control that I want to have the method "SetCurrentProgress(CString strText, double dPercentProgress)".  I primarily want to know how to make this function thread-safe, and easily accessible to my whole application (I guess making it global is the easiest/best way for easy access?)

My usual approach is to have a SendMessage call within the SetCurrentProgress function so that the message is sent to the window of the progress bar and ends up being on the GUI thread instead of whatever the calling thread is on.  But my progress bar doesn't update.  So I try doing MessageQueueHelper::DoEvents to force the events in the event loop to occur, and this seems problematic as well.....  I've had this kind of thing more or less working nicely before, but I really need to know the best way, and understand what's going wrong exactly when things don't seem to work.

Thanks for your help!
Question by:thready
    LVL 16

    Accepted Solution

    The key to being able to send a message to an object is that the object has to be associated to a  message loop.  From what I understand, any window with a window handle is going to be associated with a message loop.  But when you're talking about theads, there are "worker threads" and "UI threads".  Worker threads do not have a message loop.  A UI thread will have a message loop... it might or might not have a window or other GUI object associated with it.

    As for things being thread safe, that's just the process of using thread synchronization objects to allow multiple threads access to the same piece of memory without stepping on one another.  Somethines thread synchronization is needed, sometimes it is not.

    For example, let's say you had a thread with an abort boolean.  The perpose of the abort is to allow an outside process to set the abort flag.  The thread would periodically check to see if the abort flag had been turned on or not and terminate the thread if it was ever on.  Such a variable would not need to be made thread safe because you are only trying to set the value.  It's intended purpose isn't for one thread to set the flag and another thread reset it.  So it doesn't matter which thread sets the variable first.

    But what about a variable that held the current progress of the progress bar.  Such a variable would need to be thread safe if an outside thread increments it (as opposed to seting the value).  To increment the value, the thread would need to read the variables current value, add one to the value, and write the new value back into the variable.  But if two thread try to do that at the same time, they might only increment the value by 1 when each thread should have incremented the value by 1.  In other words, both thread could read the current value, both add 1 to that value, and both write the same value back.  Even though both threads tried to increment the value, because they both did so at the same time, the value only increased by 1.

    Now for the DoEvents:  That is a way of telling code that is running with a message loop to go process messages in the message loop.  After all, if you have a subroutine that runs a for loop from 1 to 1 million, while the code stays in that for loop, no messages (such as drag the window to a new location) get processed because the thread is busy in the for loop.  If you periodically insert DoEvents in the for loop, the loop will pause to process any messages pending in the message loop.
    LVL 33

    Assisted Solution

    by:Todd Gerbert
    My understanding is that SendMessage immediately & directly invokes the target's WndProc, where as PostMessage asks Windows to put a message in the target window's queue (which will later be retrieved by the target via the normal GetMessage/DispatchMessage) - so the latter may be necessary when sending messages between threads.
    LVL 1

    Author Comment

    You're right tgerbert- there will often be situations where SendMessage will block and cause deadlocks.  Thanks HooKooDooKu, your answer is very thorough, but there's the key element I'm still missing- where do I put the SendMessage call?  I'm creating a progress bar and I want to 1) have easy access throughout the program to the progress bar and 2) not have to worry about calling PostMessage - I'd rather call a nice function that will call PostMessage for me...  This seems easy, but I always have some trouble with it.  Like now, I have my progress bar as a global variable and the program is crashing in RegisterWindowClass (custom control stuff) on the call to AfxGetInstanceHandle() - because current instance handle is null by making the progress bar global...  I don't understand.

    So I revert to using a class variable in the parent dialog instead of making the progress bar global and now I have to worry about how I need to give my thread access to this-- which I find makes things a mess.  Globals are not the most kosher thing in the world, but they are definitely cleaner than the alternative.  After all, my app only has 1 progress bar.  Anyway.  So now I'm trying to figure out how to give this thread access to my progress bar in the most elegant way possible.

    Thread (for testing my control)
        for(int i = 0; i < 100; i++)

    inside ProgressBar::SetCurrentProgress(int nProgress)
            PostMessage(WM_PROGRESS_UPDATE, 0, nProgress)


    LONG OnProgressUpdate(WPARAM wParaam, LPARAM lParam)
            m_nProgress=  (int)lParam;

    Does this make sense?  Is there a better way?  How do I get access to the progress control in a clean way?

    Sorry for the long-winded question - but this has been bugging me for far too long and I really want to make this clear for myself.

    Thanks a lot for your help with this gentlemen!

    LVL 1

    Author Comment

    There is one alternative that I currently use that I don't like.  I just send messages and I keep a pointer to the window handle of the object that handles the message...   I just wish there were a better way....
    LVL 1

    Author Comment

    ... a more object oriented way...
    LVL 1

    Author Closing Comment

    After reading more about this online, I think it's actually better to pass HWNDs instead of accessing actual objects - it's a better way to decouple the GUI from the work being done.  So this ends this question and I'm satisfied.  Thanks again!

    Write Comment

    Please enter a first name

    Please enter a last name

    We will never share this with anyone.

    Featured Post

    What Is Threat Intelligence?

    Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

    Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
    Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
    The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
    The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.

    737 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    21 Experts available now in Live!

    Get 1:1 Help Now