how to access mfc controls using messages? - c++

I would like to access drag slider control of my dialog box from worker thread. I read many warnings regarding accessing main window controls from worker thread. Since they have a high chances of resulting in a dead lock. In this case, how to access the control using messages? I know how to create user defined messages (WM_USER) and write our own handlers. What it would like to know is that is there any system defined message for each controls and if it is there, how to post those messages to access the control?

You can use PostMessage to send a message to the control and the main UI thread will actually dispatch the message to the control, ie the work will happen on the UI thread (assuming the UI thread is pumping messages). Looking at afxcmn.inl you can see what SetPos is doing:
_AFXCMN_INLINE void CSliderCtrl::SetPos(_In_ int nPos)
{ ASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, TBM_SETPOS, TRUE, nPos); }
Changing that for PostMessage:
::PostMessage(hSlider, TBM_SETPOS, TRUE, nPos);
If you want something more complicated post a WM_USER message to a the parent window and handle your control managing functionality there because, again, the WM_USER message will be dispatched by the UI thread so you can work with your control safely.

Related

MFC: Is it safe to call CWnd methods from another thread?

Actually I have two questions:
Is it safe to call SendMessage from a worker thread?
Do CWnd methods, like MessageBox, call API function SendMessage behind the scene?
Per my understanding, when the worker thread calls SendMessage, it pushes the message into the message queue of the UI thread, and waits until this message is processed. In that case, it would be safe to do so.
I'm not quite sure about this. please correct me if I was wrong.
Thanks a lot.
------------------------ update ----------------------------------
As a conclusion:
It's safe to call the windows API ::SendMessage and ::PostMessage across threads.
It's not safe to call CWnd methods across threads. Some of the methods may be safe, but it's not guaranteed.
Great thanks to everyone.
Is it safe to call SendMessage from a worker thread?
Yes. The system makes sure, that message handling is serialized on the receiving thread. When sending messages across threads, the sender is blocked until the message has been handled. The receiver only handles a cross-thread sent message when it executes message retrieval code (GetMessage, PeekMessage, etc.). Sent messages are never queued in the message queue. The documentation for SendMessage has additional details.
Do CWnd methods, like MessageBox, call API function SendMessage behind the scene?
Yes. For one, the message box will receive standard window messages like WM_CREATE or WM_NCCREATE as part of the dialog construction. Also, for owned windows (like modal dialogs), the system will send WM_ACTIVATE messages to both the window being deactivated, and the window being activated. I'm not sure why this matters, though, or why you asked this question in particular.
Now the question in your title:
Is it safe to call CWnd methods from another thread?
In general, no. It does depend on the member, though. Some are safe to call, others aren't. In particular, all methods that modify window state (contents, visibility, activation, etc.) should only be called from the thread that created the window. In case the call is not safe, the system will still be in a consistent state. However, your application may not be.
The ONLY way for a thread to access the UI is by using SendMessage or PostMessage.
Consider a machine with one core, where context switching occurs and you make direct access to the UI from a worker thread, you are potentially corrupting the UI thread registers !
Basically every UI framework offers a mechanism (many times several), for making UI changes from a thread. For instance Android offers an ASyncTask and a Handler.

How pump COM messages in STA without pumping WM_PAINT?

I need to pump COM messages while waiting for an event to fix a deadlock. It's better to pump as few messages as possible just to process that COM call. The best candidate for this role is CoWaitForMultipleHandles but starting from Vista it pumps WM_PAINT in addition to COM messages. Pumping WM_PAINT is too dangerous for me from re-entrance perspective and I don't want to install a custom shim database as a solution for this problem.
I'm trying to pump COM messages sent to the hidden message-only window manually.
I have found two ways to get HWND of the hidden window:
((SOleTlsData *) NtCurrentTeb()->ReservedForOle)->hwndSTA using ntinfo.h from .NET Core. This seems to be undocumented and not reliable solution in terms of future changes.
Find window of OleMainThreadWndClass as suggested in this question. The problem is that CoInitialize does not create the window. It is created later on first cross-apartment call which may or may not happen in my application. Running the search loop every time I need HWND is bad from performance perspective but caching HWND seems impossible because I don't know when it's created.
Is there a way to determine if the hidden window is created for the current apartment? I suppose it will be cheaper than the loop and then I could find and cache HWND.
Is there a better way to pump COM messages without pumping WM_PAINT?
Update: you can force the window creation by calling CoMarshalInterThreadInterfaceInStream for any interface. Then call Co­Release­Marshal­Data to release the stream pointer. This is what I end up doing along with the search for OleMainThreadWndClass.
WM_PAINT is generated when there is no other message in the message queue and you execute GetMessage or use PeekMessage.
But WM_PAINT is only sent if you Dispatch it. Also there is no new WM_PAINT message until a window is invalidated again.
So it depends on you if you dispatch a WM_PAINT message or not. But be aware, there are other chances of reentrances like a WM_TIMER message.
The details about this are in the docs for WM_PAINT.
From my point of view the best solution would be to set you application in a "wait" mode, that even can handle WM_PAINT in this undefined waiting state. You know when you are reentered. It is always after a WM_PAINT... or similar messages that arrive like other input messages. So I don't see any problems here. An STA has one thread and you always process messages to an end, until you execute GetMessage, launch a modal dialog or show a MessageBox. When you are inside some message handling, nothing will disturb you.
Maybe an other solution would be to wait inside a second thread for this event. This thread may not have any windows and you can translate the event to anything you need in your application.
So you question may not have enough information how this deadlock really appears. So this answer may not be sufficient.
After writing als this I tend to the opinion that this is an XY problem.

SetScrollPos is blocking

In my multithreaded mfc app, m_view->SetScrollPos is blocking and all the app is freezed. The View is created in another thread, is this the reason for such behavior?
//SetScrollPos(SB_HORZ,pos);
::SetScrollPos(GetSafeHwnd(), SB_HORZ, pos, true);
The same happens with SetScrollInfo().
The reason is simple:
CHanging the scroll positions cause some window messages to be created. If you are in another thread and the thread hosting the window is not ready to process messages via the GetMessaage/PostMessage, the thread using SendMessage is blocked until the message can be delivered.
This is a normal and well documented behavior.
My advice: Never perform UI action from another thread. Choose a neutral communication method to inform the other thread about changes (PostMessage, Timer and data field, aso.)...

Is sending message to a non-existent window Ok?

I have a thread that send update messages to a window, I use ::SendMessage() and ::PostMessage() APIs.
I go in and out of multiple dialogs and register the dialog that I am currently in with the thread via the window handle (m_hWnd). If I exit all the way out, the main application window doesn't handle these messages. For that reason I don't register that window. At this point the thread will have the handle of an older window which now doesn't exist.
Is it okay if it sends messages to that non-existent window? I am assuming it should not do any harm but wanted to double check.
No, it is not ok to post a message to a deleted window.
There is no guarantee noone will set up shop at that address just after the previous tennant is gone.
If you use a NULL window handle, you'll post a thread message to the current threads message queue. SendMessage as far as I could google shoul be a no-op.
Might be harmless enough.
Now, we get tricky:
Under specific cicumstances it does not matter, pre-supposing well-behaved applications.
A message like WM_NULL should not make anything happen.
A window-message you globally registered in your application using RegisterWindowMessage, if you can guarantee none of your applications windows created in the meantime will choke on it.

C++ Exception Handler problem

I written an exception handler routine that helps us catch problems with our software. I use
SetUnhandledExceptionFilter();
to catch any uncaught exceptions, and it works very well.
However my handler pop's up a dialog asking the user to detail what they were doing at the time of the crash. This is where the problem comes, because the dialog is in the same thread context as the crash, the dialog continues to pump the messages of application. This causes me a problem, as one of our crashes is in a WM_TIMER, which goes off every minute. As you can imagine if the dialog has been on the screen for over a minute, a WM_TIMER is dispatched and the app re-crashes. Re-entering the exception handler under this situation is bad news.
If I let Windows handle the crash, Windows displays a dialog that appears to function, but stops the messages propagating to the rest of the application, hence the WM_TIMER does not get re-issued.
Does anyone know how I can achieve the same effect?
Thanks
Rich
Perhaps you could launch a separate data collection process using CreateProcess() when you detect an unhandled exception. This separate process would prompt the user to enter information about what they were just doing, while your main application can continue to crash and terminate.
Alternatively, if you don't want to start another process, you could perhaps create another thread with a separate message queue, that blocks your main thread from doing anything at all while the dialog is on the screen. While your main thread is blocked it won't have the opportunity to handle WM_TIMER messages.
Show the dialog in a second thread.
I had more or less the same problem (but had to show a message box rather than a dialog).
Write a class in which you create two events using the Win32 CreateEvent function. One event (trigger) is used to trigger the dialog, one event (ready) is to signal that the dialog was handled.
Add a method 'execute' to the class and start this method in a second thread
Let the 'execute' method wait until the trigger event is set, and if it is set show the dialog
After the dialog has been handled, set the 'ready' event.
If your application crashes in the main thread, prepare some information for the dialog (via setters in the class) and set the 'trigger' event, then wait for the 'ready' event. The set'ting of the trigger event will activate the second thread, and the main thread will block until the second thread has set the 'ready' event