I’ve been using Miranda for all my chats for a few years now, since the time it was a nightmare to install and configure. The chief reason I like it is the fact that it supports Hebrew amazingly well, via the TabSRMM plugin. You can even set the right-to-left settings for the chat window on a person-by-person basis.
However, the latest version of Miranda broke my language switching gizmo, Recaps. Every once in a while the CapsLock key would stop switching the language in the Miranda chat window, and even restarting Recaps didn’t help. The basic thing Recaps does upon detecting that CapsLock has been pressed is send an WM_INPUTLANGCHANGEREQUEST message the the current foreground window, which is obtained via the GetForegroundWindow Win32 API function. Apparently I overlooked the fact that the MSDN specifically says the aforementioned message is sent to the window that has the keyboard focus, which I clear wasn’t doing. GetForegroundWindow just gets you the current top-level window, not the actual control that has the keyboard focus.
All I had to do was just replace the GetForegroundWindow call something that gets the window that has the focus and be done with it. And GetFocus seemed to be just what I needed. Only there was a catch:
The GetFocus function retrieves the handle to the window that has the keyboard focus, if the window is attached to the calling thread’s message queue.
That isn’t good enough of course as the whole point was finding out which window had the focus when that window belonged to a different application, running in a different process, in a different thread. There are a few techniques to inject code into a remote process, but they all require creating a separate DLL and tricking the target process into loading it either via hooks or using the CreateRemoteThread/LoadLibrary trick. I was getting ready to dive into that dark abyss when I stumbled upon the AttachThreadInput function:
Windows created in different threads typically process input independently of each other. That is, they have their own input states (focus, active, capture windows, key state, queue status, and so on), and they are not synchronized with the input processing of other threads. By using the AttachThreadInput function, a thread can attach its input processing to another thread. This also allows threads to share their input states, so they can call the SetFocus function to set the keyboard focus to a window of a different thread. This also allows threads to get key-state information. These capabilities are not generally possible.
So, all I had to do was call AttachThreadInput to connect my main UI thread to the thread responsible for the current foreground window, call GetFocus to find out which window has the focus, and then call AttachThreadInput again to give the control of the input back to the original thread.
Here’s the code to do that:
HWND hwnd = GetForegroundWindow();
DWORD remoteThreadId = GetWindowThreadProcessId(hwnd, NULL);
DWORD currentThreadId = GetCurrentThreadId();
AttachThreadInput(remoteThreadId, currentThreadId, TRUE);
HWND focused = GetFocus();
AttachThreadInput(remoteThreadId, currentThreadId, FALSE);
This function will only work if called from a thread that has created a message queue, which is the case for a standard Win32 main thread that runs in a message loop.
Miranda and Recaps now play well together.