How to obtain keyboard layout for Microsoft Edge and other windows hosted in ApplicationFrameHost.exe
Asked Answered
K

1

2

As we all know, Windows's keyboard layouts are thread-specific. When a layout changes, a message is dispatched to the foreground thread by the shell. So if one wants to obtain the most recent system-wide keyboard layout, one must do something like this:

const HWND foregroundWindow = ::GetForegroundWindow();
const DWORD foregroundThread = ::GetWindowThreadProcessId(foregroundWindow, NULL);
const HKL layout = ::GetKeyboardLayout(foregroundThread);

This works fine for most native Windows apps.

However, UWP applications, and several system apps including Microsoft Edge host their windows in the ApplicationFrameHost.exe. This causes ::GetForegroundWindow() to return the window of said process, leading to several curious problems, including misdetection of the foreground process and troubles detecting the actual keyboard layout.

The threads of ApplicationFrameHost.exe simply don't react to the system-wide keyboard layout change. It seems that they are not hosting the actual focused UI element. ::GetForegroundWindow() returns only the frame window, but the actual UI element has its own thread and maybe is hosted by its own process. Thus, the foreground framw window is simply not getting the respective layout change messages, and its thread has a stale HKL associated with it.

How can I detect the correct HKL of the foreground process?

Kokanee answered 21/8, 2018 at 9:41 Comment(0)
K
3

The trick was to stop relying on the GetForegroundWindow() method altogether.

Instead, I solved the problem by utilising the quirky and counter-intuitive, but documented specific usecase of GetGUIThreadInfo().

If you pass zero as the first argument to this function, it will return the information for the foreground thread – the one that is receiving the actual user input!

This thread will also receive timely HKL-related messages from the shell, so the keyboard layout will not be stale.

GUITHREADINFO gti = { sizeof(GUITHREADINFO) };

// fetching the foreground thread info
::GetGUIThreadInfo(0, &gti);

// you may also fallback to other window handles in the GUITHREADINFO
// if the `hwndFocus == NULL`
const DWORD thread = ::GetWindowThreadProcessId(gti.hwndFocus, NULL);
const HKL layout = ::GetKeyboardLayout(thread);
Kokanee answered 21/8, 2018 at 9:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.