为什么GetKeyboardLayoutName在布局更改后返回相同的名称



在下面编写的这个程序中,使用user32.dll中的GetKeyboardLayoutName。当我用";English USA";布局,我得到00000409。这很好。但当我把我的布局改成其他东西时,比如";English UK";或";俄语";,GetKeyboardLayoutName返回";英语美国"-00000409.

我对此进行了测试,如果我在"0"中输入第一个符号;俄语";,它返回00000419,并且如果我切换回"0";English USA";并且输入第二个符号,GetKeyboardLayoutName仍然返回"0"的代码;俄语"-00000419.

using System;
using System.Runtime.InteropServices;
using System.Text;
namespace ConsoleApp2
{
class Program
{
const int KL_NAMELENGTH = 9;
[DllImport("user32.dll")]
private static extern long GetKeyboardLayoutName(StringBuilder pwszKLID);
public static string GetLayoutCode()
{
var name = new StringBuilder(KL_NAMELENGTH);
GetKeyboardLayoutName(name);
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);
}
}
}

问题是控制台应用程序不允许您处理windows消息
您可以将您的应用程序转换为winforms应用程序,使主窗体隐藏,并使子类WndProc处理键盘布局更改时触发的WM_INPUTLANGCHANGE消息:

public class MyHiddenForm : Form
{
...
protected override void WndProc(ref Message m)
{
const int WM_INPUTLANGCHANGE = 0x0051;
if (m.Msg == WM_INPUTLANGCHANGE)
{
Console.WriteLine("{0:X8} {1:X8}", m.WParam.ToInt64() , m.LParam.ToInt64());
}
base.WndProc(ref m);
}
}

另一种方法是通过控制台应用程序中的WinAPI创建一个隐藏窗口,该窗口将接收消息。但我个人更喜欢第一种方法。

如果您不需要KLID(键盘布局标识符(,而只需要一种识别键盘布局的方法,那么您可以使用以下方法来获取HKL(输入区域设置标识符(:

var focusedHWnd = GetForegroundWindow();
var activeThread = GetWindowThreadProcessId(focusedHWnd, IntPtr.Zero);
var hkl = GetKeyboardLayout(activeThread);

这是因为GetKeyboardLayoutName()正在请求当前线程的布局(每个线程都有自己的键盘布局,这就是为什么我们有AttachThreadInput()等API(。您的控制台应用程序没有带有Win32窗口的线程。只有聚焦的Win32窗口才能通过热键以本机方式更改其键盘布局。据我所知,没有办法在控制台窗口中检测到这种语言变化,因为它们托管在特殊的cmd.exe子窗口(或其他终端应用程序(中。

最新更新