user4582812
user4582812

Reputation: 621

How to access the UI controls from another thread?

I know that in order to access the UI controls from another thread, I should use PostMessage(). However, PostMessage() is asynchronous, so for example if I try to change the text of an "EDIT" control, I will not be able to delete the text buffer when finished because I do not know when the window procedure will finish processing the message.

So these are two ideas I can think of to access the UI controls from another thread:

Does one of these approaches provide some advantages over the other, and are there other approaches to do this?

Upvotes: 1

Views: 667

Answers (2)

JensG
JensG

Reputation: 13401

I frequently use the following pattern (the arrows indicate a "uses" relationship):

           +---------------+                     
           | Communication |        +-----------+
      +--->+  Data Object  +<---+---+ Thread #0 |
      |    | (thread safe) |    |   +-----------+
      |    +---------------+    +---+ Thread #1 |
      |                         |   +-----------+
      |                         |     ...        
+-----+----+                    |   +-----------+
| Main (UI)|                    +---+ Thread N  |
|  Thread  |                        +-----------+
+----------+                                     

The Communication Data object is threadsafe and typically ref-counted (important for non-GC-languages). The object provides a couple of typical methods (all optional, depending on the actual use case):

  • work item queue
  • data getter/setter for any other data that needs to be exchanged
  • progress information to be visualized in the UI
  • an abort/status flag, combined with ...
  • waitable event object to signal various things

Because the data object fully takes care of itself, including synchronizing access to its data, and more or less automatic cleanup due to ref-counting, you end up with all that thread stuff nicely encapsulated into one dedicated class. I found this very handy in various situations.

Furthermore, the approach fully decouples the worker threads from the UI, so the threads don't even know whether or not there is an UI at all and how it looks like: is it a GUI? Is it a CLI? A web service maybe? Last not least it keeps the UI responsive, because the UI thread (or the equivalent thereof) can fully decide on its own when and how often to update the UI.


PS: Probably there's an official GOF name for it, don't know.

Upvotes: 3

David Heffernan
David Heffernan

Reputation: 612774

I know that in order to access the UI controls from another thread, I should use PostMessage(). However, PostMessage() is asynchronous, so for example if I try to change the text of an "EDIT" control, I will not be able to delete the text buffer when finished because I do not know when the window procedure will finish processing the message.

You are mistaken. There is no compulsion to use PostMessage. And indeed for setting the window text of a control, you should not use PostMessage. You need to send WM_SETTEXT synchronously, for the very reasons you outline. If you don't send it synchronously then you don't know when to destroy the text buffer.

What you need to do is as follows:

  • If the window is in your process, then you should use SetWindowText.
  • If the window is in a different process, then you should use SendMessageTimeout to send the WM_SETTEXT message.

For windows in a different process, SetWindowText is documented not to work. It's a little more complex than that, as Raymond explains, but you still, as a rule, should not use it on a window in a different process. So, for windows in a different process, use SendMessageTimeout to send WM_SETTEXT. The timeout is to prevent your application become hung if the target application is hung.

Upvotes: 2

Related Questions