首页 > 解决方案 > How to obtain keyboard layout for Microsoft Edge and other windows hosted in ApplicationFrameHost.exe

问题描述

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?

标签: windowswinapiuwpmicrosoft-edgekeyboard-layout

解决方案


诀窍是完全停止依赖该GetForegroundWindow()方法。

相反,我通过利用古怪和违反直觉但记录了GetGUIThreadInfo().

如果您将零作为第一个参数传递给此函数,它将返回前台线程的信息——接收实际用户输入的线程!

该线程还将及时从 shell 接收与 HKL 相关的消息,因此键盘布局不会过时。

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);

推荐阅读