Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change GUI in thread

I have an operation which ends in about 20 seconds. To avoid freezing, I want to create a thread and update a label text in it every second. I searched a lot, since everyone has different opinion, I couldn't decide which method to use.

I tried SendMessage and it works but some people believe that using SendMessage is not safe and I should use PostMessage instead. But PostMessage fails with ERROR_MESSAGE_SYNC_ONLY (1159).

char text[20] = "test text";
SendMessage(label_hwnd, WM_SETTEXT, NULL, text);

I searched about this and I think it's because of using pointers in PostMessage which is not allowed. That's why it fails.

So, what should I do? I'm confused. What do you suggest? Is this method is good for change UI elements in other thread?

Thanks

like image 233
Malik Çelik Avatar asked Dec 02 '22 20:12

Malik Çelik


2 Answers

The documentation for ERROR_MESSAGE_SYNC_ONLY says:

The message can be used only with synchronous operations.

This means that you can use synchronous message delivery, i.e. SendMessage and similar, but you cannot use asynchronous message delivery, i.e. PostMessage.

The reason is that WM_SETTEXT is a message whose parameters include a reference. The parameters cannot be copied by value. If you could deliver WM_SETTEXT asynchronously then how would the system guarantee that the pointer that the recipient window received was still valid?

So the system simply rejects your attempt to send this message, and indeed any other message that has parameters that are references.

It is reasonable for you to use SendMessage here. That will certainly work.

However, you are forcing your worker thread to block on the UI. It may take the UI some time to update the caption's text. The alternative is to post a custom message to the UI thread that instructs the UI thread to update the UI. Then your worker thread thread can continue its tasks and let the UI thread update in parallel, without blocking the worker thread.

In order for that to work you need a way for the UI thread to get the progress information from the worker thread. If the progress is as simple as a percentage then all you need to do is have the worker thread write to, and the UI thread read from, a shared variable.

like image 143
David Heffernan Avatar answered Dec 16 '22 17:12

David Heffernan


Well, the error says it all. The message cannot be sent asynchronously. The thing about PostMessage is that it posts the message to the listening thread's queue and returns immediately, without waiting for the result of message processing. SendMessage on the other hand, waits until the window procedure finishes processing the message and only then it returns.

The risk of using PostMessage in your case is that before window procedure processes the message you may have deallocated the string buffer. So it is safer to use SendMessage in this instance and that's what MS developers probably thought about when they decided not to allow asynchronous posting of this particular message.

EDIT: Just to be clear, of course this doesn't eliminate the risk of passing a naked pointer totally.

like image 33
W.B. Avatar answered Dec 16 '22 17:12

W.B.