如何获取 ApplicationFrameHost.exe 中托管的 Microsoft Edge 和其他 windows 的键盘布局

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

众所周知,Windows 的键盘布局是特定于线程的。当布局发生变化时,shell 会向前台线程发送一条消息。因此,如果想要获得最新的系统范围键盘布局,则必须执行以下操作:

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

这适用于大多数原生 Windows 应用程序。

但是,UWP 应用程序和几个系统应用程序(包括 Microsoft Edge)在 ApplicationFrameHost.exe 中托管它们的 windows。这会导致上述过程的 ::GetForegroundWindow() 到 return 和 window,从而导致几个奇怪的问题,包括 和检测实际键盘布局的麻烦。

ApplicationFrameHost.exe 的线程根本不会对系统范围内的键盘布局更改做出反应。似乎他们没有托管实际的重点 UI 元素。 ::GetForegroundWindow() return 只是框架 window,但实际的 UI 元素有自己的线程,并且可能由自己的进程托管。因此,前台框架 window 根本没有收到相应的布局更改消息,并且它的线程有一个与之关联的陈旧 HKL

如何检测前台进程的正确 HKL?

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

相反,我利用古怪和违反直觉的方式解决了问题,但是 documented specific usecase of GetGUIThreadInfo()

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

本帖也会及时收到来自 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);