为什么 GetKeyboardLayoutName return 在布局更改后名称相同?
Why does GetKeyboardLayoutName return the same name after a layout change?
在下面编写的程序中,使用了 user32.dll 中的 GetKeyboardLayoutName。
当我使用“English USA”布局键入第一个符号时,我得到 00000409。这很好。
但是当我将布局更改为其他内容时,例如“英国英语”或“俄语”,GetKeyboardLayoutName returns“美国英语”的代码 - 00000409。
我对此进行了测试,如果我在“俄语”中输入第一个符号,它 returns 00000419 如果我切换回“美国英语”并输入第二个符号,GetKeyboardLayoutName 仍然 returns “俄语”代码 - 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 创建一个隐藏的 window。但我个人更喜欢第一种方法。
如果您不需要 KLID(键盘布局标识符),而您只需要一种识别键盘布局的方法,那么您可以使用以下方法获取 HKL(输入区域设置标识符):
var focusedHWnd = GetForegroundWindow();
var activeThread = GetWindowThreadProcessId(focusedHWnd, IntPtr.Zero);
var hkl = GetKeyboardLayout(activeThread);
那是因为 GetKeyboardLayoutName()
正在请求当前线程的布局(每个线程都有自己的键盘布局,这就是为什么我们有 AttachThreadInput()
等 API)。您的控制台应用程序没有 Win32 window 线程。只有重点关注的 Win32 windows 可以通过热键本机更改其键盘布局。据我所知,无法在控制台 windows 中检测到此类语言变化 - 因为它们托管在特殊的 cmd.exe
子 window(或其他终端应用程序)中。
在下面编写的程序中,使用了 user32.dll 中的 GetKeyboardLayoutName。 当我使用“English USA”布局键入第一个符号时,我得到 00000409。这很好。 但是当我将布局更改为其他内容时,例如“英国英语”或“俄语”,GetKeyboardLayoutName returns“美国英语”的代码 - 00000409。
我对此进行了测试,如果我在“俄语”中输入第一个符号,它 returns 00000419 如果我切换回“美国英语”并输入第二个符号,GetKeyboardLayoutName 仍然 returns “俄语”代码 - 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 创建一个隐藏的 window。但我个人更喜欢第一种方法。
如果您不需要 KLID(键盘布局标识符),而您只需要一种识别键盘布局的方法,那么您可以使用以下方法获取 HKL(输入区域设置标识符):
var focusedHWnd = GetForegroundWindow();
var activeThread = GetWindowThreadProcessId(focusedHWnd, IntPtr.Zero);
var hkl = GetKeyboardLayout(activeThread);
那是因为 GetKeyboardLayoutName()
正在请求当前线程的布局(每个线程都有自己的键盘布局,这就是为什么我们有 AttachThreadInput()
等 API)。您的控制台应用程序没有 Win32 window 线程。只有重点关注的 Win32 windows 可以通过热键本机更改其键盘布局。据我所知,无法在控制台 windows 中检测到此类语言变化 - 因为它们托管在特殊的 cmd.exe
子 window(或其他终端应用程序)中。