众所周知,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?
答案 0 :(得分:2)
诀窍是完全不再依赖GetForegroundWindow()
方法。
相反,我通过使用古怪且违反直觉的documented specific usecase of GetGUIThreadInfo()
解决了这个问题。
如果传递零作为该函数的第一个参数,它将返回有关前台线程的信息-正在接收实际用户输入的信息!
此线程还将从外壳程序及时接收与HKL相关的消息,因此键盘布局不会过时。
GUITHREADINFO gti = { sizeof(GUITHREADINFO) };
// fetching the foreground thread info
::GetGUIThreadInfo(0, >i);
// 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);