When a modal dialog box is present, messages sent to the main window using PostMessage do not go through that window's message loop. In fact, no message go through the main window's message loop while the modal dialog is present. They do make it to the main window's WndProc presumably through some message loop in the dialog handling.
Is this normal or am I doing something bizarre?
This is normal, the dialog box becomes modal by pumping its own message loop. This should not be a problem, DispatchMessage still delivers the message to the window procedure. Make sure you post with a valid window handle. Otherwise also the reason that PostThreadMessage() is a guaranteed fail whale if the thread creates any windows. Like MessageBox().
Related
My goal is to disable some tool-tip-like thing in window of another process. My thoughts are to block WM_MOUSEMOVE if the coordinates of mouse movement is in certain rectangle of the window.
I can use SetWindowsHookEx to receive that messages (WH_CALLWNDPROC and WH_GETMESSAGE), but it doesn't allow to block them. I can return 1 instead of calling CallNextHookEx, but it doesn't prevent WndProc of window to receive the message. However, this approach works for blocking events in MOUSE_LL/KEYBOARD_LL.
SetWindowsHookEx is also not good, because it installs hook to all windows across the system.
How I can prevent window of another process from receiving certain windows messages?
The WH_GETMESSAGE message hook doesn't let you "block" or cancel the message but you can modify it. So simply change lpMsg->message to WM_NULL to prevent the mouse move message from being processed by the target window.
My understanding is any modal dialog automatically has its own message-pump, running on a thread dedicated to that dialog - is that right?
If so, how does a modal dialog's existence affect the main application's message loop? Do both run in parallel, does one take priority?
I have a situation where a modal dialog seems to get stuck for several seconds waiting for something, and wondered if it's possible the dialog is forced to wait until the main application thread is not busy?
As IInspectable explained, the modal dialog will run in the same thread as the caller. Therefore if you run the dialog from the main UI thread that has your main message loop, you'll end up with a nested message loop. The stack would look something like:
WinMain
YourMainMessageLoop
DispatchMessage
SomeMessageHandler
DoModal
and DoModal spins in its own GetMessage/TranslateMessage/DispatchMessage loop. The main message loop (YourMainMessageLoop in the sample stack above) is "active" in the sense that it's still running, but it's blocked by the dialog's message loop. Execution won't return to YourMainMessageLoop until DoModal exits.
Note that even if you're within the modal dialog's message loop, your other windows will still handle messages because GetMessage and DispatchMessage will still retrieve and direct messages to those windows and invoke their WndProcs.
I'm a bit confused currently: Are WM_CLOSE and ::CloseWindow in any way "related" or are for completely different things?
The docs for Closing Windows don't mention the API function CloseWindow at all. Should CloseWindow be really called "MinimizeWindow" or what am I missing?
CloseWindow and WM_CLOSE are completely unrelated. The CloseWindow function is badly named. Its inverse function, OpenWindow is similarly badly named given that it restores windows.
I suspect these names dates back a very long way indeed, probably to Windows version 1 or 2. I'm speculating that what we now call minimize and restore were, back then, called close and open.
The usual way to minimize or restore a window is to call ShowWindow passing SW_MINIMIZE or SW_RESTORE.
I suggest that you forget all about CloseWindow and OpenWindow.
CloseWindow is an unusually poorly named winapi function. It doesn't actually close a window, it just minimizes it. What you possibly meant was DestroyWindow().
WM_CLOSE is normally a message that's generated by default window procedure, in response to the user pressing Alt+F4 or clicking the window's close button. The underlying message is WM_SYSCOMMAND, SC_CLOSE. It can be generated in code as well, like a Window + Close menu item.
You can listen for WM_CLOSE in your window procedure or the MFC message map. The user will expect the window to be closed. So you normally call DestroyWindow(). You don't have to, you might display a message box for example and ask the user if data should be saved. And if he clicks No then you don't call DestroyWindow().
I would say ignore CloseWindow and OpenWindow() for the reasons the previous poster suggested. However, while technically doable, it is considered bad form to handle whether to save or actually destroy a window (ie prompting the user) during WM_DESTROY. Once DestroyWindow is called and the OS sends a WM_DESTROY message, the expectation is to prepare for the destruction of the window. So the workflow should have be like this:
Send a WM_CLOSE to the window handle in question. CloseWindow SHOULD do this, like DestroyWindow sends a WM_DESTROY message or ShowWindow sends a WM_SHOWWINDOW message. But again, since it doesn't, send the WM_CLOSE to the window.
Next, during the processing of the WM_CLOSE message, you can ask the user if they wish to save anything, or if they intend to actually close the window. If they cancel closing the window, simply return ZERO to tell the OS you processed the message. If the user indicates they wish to proceed, THEN you can call DestroyWindow OR simply pass the message to DefWindowProc, which the promptly do the same. Any saving of files should happen during the processing of WM_CLOSE. During the processing of WM_DESTROY, you should delete any dynamically allocated memory associated with the window and free resources. And finally FYI, WM_NCDESTROY is the last message a window receives.
ShowWindow(g_hWnd, 1);
UpdateWindow(g_hWnd);
I am wondering why we need to call UpdateWindow following the ShowWindow?
It is entirely unnecessary, your window will paint just fine without it.
You'll see a minor benefit from it if your program goes off doing lots of stuff after creating the window but before entering the message loop. The user has something to look at. A splash screen is the more typical approach.
ShowWindow does not repaint the window. The call to UpdateWindow sends WM_PAINT message to the window and thus repainting it.
Normally, the system sends WM_PAINT only if the message queue is empty. Under normal circumstances this is good enough and it actually optimizes out a lot of unnecessary repaint. The messages in the queue often will change application state which can often result in invalidating part of the window, and hence result in yet another painting (so the user sees the new application state). So the repaint just happens after all such messages are handled and the system thinks the new window content will be valid for some time (until yet another message(s) come into the queue).
However if you need to force the WM_PAINT immediately and bypass the logic above, you may force sending WM_PAINT (if there is an invalid region) by calling UpdateWindow().
ShowWindow causes a WM_PAINT message, whereas anything which makes a previously hidden part of a window visible. UpdateWindow does any outstanding paint message(s) to be delivered immediately, waiting until the paint processing is complete until returning. Without the call to UpdateWindow the message isn't received until your program goes into the message loop. This avoids any possible delay in response to the user.
We have a MFC Visual-C++ application that is not reacting to any user input.
(Note: Currently only known on one machine. The behavior does recur occasionally, but only after the App has been running for several days!)
The application is redrawn when we switch to it via Alt-Tab (or the Task Bar), but we cannot, for example, activate it's main window by clicking on the title bar.
We have already pulled 4 dumps with WinDbg and checked the active instructions. We always were in some redraw code or somesuch inside the main thread (the GUI thread). We definitely were/are not in a modal message loop and the main thread's stack always looked "OK". (Most/all worker threads were idling, waiting for some event, no suspicious code there either.)
When investigating the problem with Spy++, we see the behavior specified also in this separate question, namely that we seem to get paint and activation messages, but no user input is routed to the application. When I have the application window on the screen, and select it to show messages of the main window,
it will only show "generic" "referesh" messages and nothing else
If I drill deeper, and select all messages for the whole process,
this is what we see:
The app is apparently only processing messages on one hidden sub-window (00CB09F0), and what we see there is a constant stream of 200 WM_PAINT messages per second.
Normally this Sub Window isn't processing any messages at all (except refresh WM_PAINT etc. when Windows sends them). It is normally used as a drawing area and the drawing happens through a WM_TIMER message on it's parent (010A09B8) window. (This WM_TIMER message isn't shown on the hanging app either though.)
The performance profile as shown in process explorer looks like this (100% kernel time, more or less):
I'd say that you have a redraw loop in that window that receives the WM_PAINT flood.
That usually happens if you call Invalidate or similar from the processing of the WM_PAINT message, directly or indirectly.
Other posibility is that, since you say that you are using a timer to redraw the window, the actual drawing is taking more time that the time it self, so the messages pile up in the queue.
Yet another posibility is that you are invalidating the window from a different thread than the one making the painting.
Anyway, you should ensure that you are calling Invalidate*() properly (you showed no code), and never from the OnPaint event. And avoid calling UpdateWindow() as this function can mess things if called without a little care.
I've seen this problem when an exception is thrown from a dialog. MFC's DoModal function disables the main program window then reenables it when the dialog returns; exceptions bypass the reenabling part and the main window remains disabled forever.