A customer reported that their program hung when the user changed keyboard layouts, say by using the Win+Space hotkey sequence. They debugged it as far as observing that the foreground window in their application received a WM_, and when that message was passed to DefÂWindowÂProc, the call never returned. What’s so haunted about the WM_ message?
What’s so haunted about it is that the default behavior of the WM_ message is to change input language!
For historical (and therefore now compatibility) reasons, when a hotkey-initiated input language change request is accepted, the system applies the change to all threads of that process. This means that all UI threads of the process need to be pumping messages so that they can receive the notification that their keyboard state has changed.
In this case, the customer had a background thread that created a window but was not pumping messages. That prevented the language change from completing and caused the main UI thread to hang.
The customer wanted to know if there was a way to configure their program so that hotkey-initiated input language changes don’t require all threads to be pumping messages. But that’s trying to solve too narrow a problem. If your thread has created a window, then it must pump messages. Today it’s causing trouble with input language changes. Tomorrow, it’s going to cause problems with DDE, and the day after tomorrow, it’s going to cause problems with theme changes.
Even if you had a way to change the way language changes work, that’s just one of the problems that your non-responding thread is causing. You should fix the root cause: Either pump messages or destroy the window so that it is no longer a UI thread and is no longer obligated to pump messages.
In some cases, the user might even not be aware that some API they called created a window.
I don't write any code targeting Windows or other Microsoft products. I also don't write any GUI code. But even I knew that you have to pump messages if you create a window. I'm baffled that this customer apparently thought they could "just" opt out of the central paradigm of Windows GUI programming.
Taking a wild guess: Some part of this code originated in a TUI application that never had to pump messages (because TUIs are synchronous), and the original design intermingled the business logic with the UI. Instead of totally rewriting it, they tried to incrementally translate the TUI code...
Many years ago, I hit something along the same lines. I had a background MTA thread with no windows or message pump. This thread called VariantChangeType, which is in the OLE Automation runtime and has locale-dependent behaviour. When this was the first locale-dependent call in the process, the OLE Automation runtime created a hidden window to watch for the user changing their locale settings in Control Panel. Since there was no message pump, this caused various mysterious hangs.
My workaround was to make a dummy call to VariantChangeType early in the main thread so that the window...
If it’s any consolation, the issue has been long since fixed. (That report is from 1998.)
I love these old stories (and lived many of them). Even if I didn’t encounter a specific issue, I always read the *why* of how the bug arose, consider how it was originally written, and the best back-compat manner of fixing it. It’s a useful skill I apply reflexively today across wildly different programming & runtime environments, and life generally. Thanks for the story & link!
What happened to the blog entry for May 12?
Something glitched in the publishing pipeline. I retroactively published it. Thanks for letting me know.