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

时间:2018-08-21 09:41:32

标签: windows winapi uwp microsoft-edge keyboard-layout

众所周知,Windows的键盘布局是特定于线程的。当布局更改时,shell会将消息调度到前台线程。因此,如果要获取最新的全系统键盘布局,则必须执行以下操作:

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

这对于大多数本机Windows应用程序都适用。

但是,UWP应用程序和包括Microsoft Edge在内的一些系统应用程序将其窗口托管在ApplicationFrameHost.exe中。这导致::GetForegroundWindow()返回上述过程的窗口,从而导致一些奇怪的问题,包括misdetection of the foreground process和检测实际键盘布局的麻烦。

ApplicationFrameHost.exe的线程根本不会对系统范围的键盘布局更改做出反应。看来他们没有托管实际的聚焦UI元素。 ::GetForegroundWindow()仅返回框架窗口,但是实际的UI元素具有其自己的线程,并且可能由其自己的进程托管。因此,前景框架窗口根本没有获得相应的布局更改消息,并且其线程具有与之关联的陈旧HKL

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

1 个答案:

答案 0 :(得分:2)

诀窍是完全不再依赖GetForegroundWindow()方法。

相反,我通过使用古怪且违反直觉的documented specific usecase of GetGUIThreadInfo()解决了这个问题。

如果传递零作为该函数的第一个参数,它将返回有关前台线程的信息-正在接收实际用户输入的信息!

此线程还将从外壳程序及时接收与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);