为什么切换键盘布局后GetKeyboardLayoutName返回值不变?
问题根因
GetKeyboardLayoutName的P/Invoke签名定义错误:该Win32 API的返回值为表示调用是否成功的BOOL类型,对应C#的bool或者int,你写的long会导致栈结构异常,干扰调用结果。GetKeyboardLayoutName本身的特性限制:它只能获取调用它的线程所绑定的键盘布局,而控制台程序的业务线程不会自动同步系统全局的键盘布局切换。系统的布局切换通知只会发送到前台GUI窗口的所属线程,控制台窗口由系统进程托管,你自行实现的控制台线程不会主动更新绑定的布局,因此永远返回第一次调用时的布局结果。
修复方案
如果需要实时获取当前激活的键盘布局ID,需要先获取前台窗口绑定的最新布局,再读取对应ID,修复后的代码如下:
using System; using System.Runtime.InteropServices; using System.Text; namespace ConsoleApp2 { class Program { const int KL_NAMELENGTH = 9; [DllImport("user32.dll")] private static extern bool GetKeyboardLayoutName(StringBuilder pwszKLID); [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [DllImport("user32.dll")] private static extern IntPtr GetKeyboardLayout(uint idThread); [DllImport("user32.dll")] private static extern IntPtr ActivateKeyboardLayout(IntPtr hkl, uint Flags); public static string GetLayoutCode() { // 获取当前前台窗口的线程ID IntPtr hwnd = GetForegroundWindow(); uint threadId = GetWindowThreadProcessId(hwnd, out _); // 获取前台线程当前绑定的键盘布局句柄 IntPtr currentHkl = GetKeyboardLayout(threadId); // 方案1:如果只需要数字编码,直接取布局句柄低16位转16进制即可,无需调用GetKeyboardLayoutName // return currentHkl.ToInt32().ToString("x8"); // 方案2:如果需要调用GetKeyboardLayoutName获取完整布局名,临时切换线程布局 IntPtr oldHkl = ActivateKeyboardLayout(currentHkl, 0); var name = new StringBuilder(KL_NAMELENGTH); GetKeyboardLayoutName(name); ActivateKeyboardLayout(oldHkl, 0); return name.ToString(); } static void Main(string[] args) { Console.ReadKey(); var res = GetLayoutCode(); Console.WriteLine("\n" + res); Console.ReadKey(); res = GetLayoutCode(); Console.WriteLine("\n" + res); } } }
内容的提问来源于stack exchange,提问作者Grey
相关产品推荐
相关产品推荐

