问题
GetKeyboardLayout
为什么将(格式化为十六进制后)的输出传递到LoadKeyboardLayout
不起作用?
文档GetKeyboardLayout
说(我加粗的部分):
获取键盘布局
检索活动输入区域标识符(以前称为键盘布局)。
返回值
类型: HKL
返回值是输入区域标识符用于线程。 低字包含语言标识符输入语言和 高位字包含设备句柄键盘的物理布局。
以及LoadKeyboardLayout
说(我加粗的部分):
加载键盘布局A
将新的输入法区域标识符(以前称为键盘布局)加载到系统中。
句法
HKL LoadKeyboardLayoutA( LPCSTR pwszKLID, UINT Flags );
参数
pwszKLID
类型:LPCTSTR
输入区域标识符的名称要加载的。此名称是由语言标识符(低位字)和一个设备标识符(高位字)。例如,美国英语的语言标识符为 0x0409,因此主要的美国英语布局被命名为“00000409”。美国英语布局的变体(例如 Dvorak 布局)被命名为“00010409”、“00020409”等等。
我理解上面加粗文字的方式是,将GetKeyboardLayout
(格式化为十六进制后)的输出传递给LoadKeyboardLayout
应该可以工作,但事实并非如此。
例子
假设:
- 您的默认键盘语言是美国英语(可以是其他任何语言)。
- 您当前的键盘语言是俄语(可以是其他任何语言)。
现在,继续运行以下 AutoHotKey 脚本:
^1::run()
run() {
currentKL := getCurrentKeyboardLayout()
currentKL_hex := Format("{:#x}", currentKL)
currentKL_hexsub := Format("{:08}", SubStr(currentKL_hex, 3))
resultKL := Format("{:#x}", LoadKeyboardLayout(currentKL_hexsub))
}
getCurrentKeyboardLayout() {
WinGet, winId,, A
threadId := DllCall("GetWindowThreadProcessId", "uint", winId, "uint", 0)
inputLocaleIdentifierId := DllCall("GetKeyboardLayout", "uint", threadId, "uint")
Return inputLocaleIdentifierId
}
loadKeyboardLayout(inputLocaleIdentifierName) {
KLF_ACTIVATE := 1
inputLocaleIdentifierId := DllCall("LoadKeyboardLayout", "Str", inputLocaleIdentifierName, "uint", KLF_ACTIVATE)
Return inputLocaleIdentifierId
}
打印getKeyboardLayout
的变量给出:
currentKL: 68748313 <-- Russian identifier, int, output of GetKeyboardLayout
currentKL_hex: 0x4190419 <-- Russian identifier, hex
currentKL_hexsub: 04190419 <-- Russian identifier, hex, input for LoadKeyboardLayout
resultKL: 0x4090409 <-- English US identifier, hex, output of LoadKeyboardLayout
哎呀!
值resultKL
是英语美国标识符而不是俄语标识符,这意味着失败!
以下是文档(我加粗了):
如果函数成功,返回值是与 pwszKLID 中指定的名称相对应的输入区域标识符。如果没有可用的匹配语言环境,则返回值为系统的默认语言。
问题
如果我们通过了00000419
(俄罗斯列出的标识符这里和这里) 而loadKeyboardLayout
不是传递04190419
,我们就会得到期望的结果。
然而,这对其他语言的布局不起作用。
以印地语为例:
- 如果当前键盘布局为 Hindi Traditional,则
getCurrentKeyboardLayout
输出0xf00c0439
。将此值(省略前缀0x
)传递给loadKeyboardLayout
不起作用,但传递标价00010439
将要。 - 如果当前键盘布局为 Hindi Phonetic,
getCurrentKeyboardLayout
则输出0x4090439
。将此值(省略前缀0x
)传递给loadKeyboardLayout
不起作用,由于 Hindi Phoentic 没有列出值,因此我们无法从其他地方获取它。
LoadKeyboardLayout
如果它不愿意使用完整的标识符,我们如何加载这些(或其他)布局?
我想我遗漏了一些基本的东西,所以请启发我。
答案1
我忽略了所引用的两个文档之间的区别:
GetKeyboardLayout
返回值是输入语言环境标识符,而LoadKeyboardLayout
输入值应该是姓名输入法区域标识符。
GetKeyboardLayout
返回一个被称为“输入区域标识符”类型的变量HKL
(代表“键盘布局句柄”),同时LoadKeyboardLayout
接收一个类型为的参数LPCSTR
(名为pwszKLID
,其中pwsz
代表“指向宽字符串的指针,以零终止”(匈牙利表示法)和KLID
代表“键盘布局标识符”)。
KLID
现在,要从a 中获取 a HKL
,我们需要使用GetKeyboardLayoutName
。遗憾的是,它不接收 aHKL
作为参数,而只检索当前活动的输入语言环境标识符的名称 - 但我们可以使用我找到的这段代码这里:
getKLIDfromHKL(HKL) {
VarSetCapacity(KLID, 8 * (A_IsUnicode ? 2 : 1))
priorHKL := DllCall("GetKeyboardLayout", Ptr,DllCall("GetWindowThreadProcessId", Ptr,0, UInt,0, Ptr), Ptr)
if !DllCall("ActivateKeyboardLayout", Ptr, HKL, UInt,0) ||
!DllCall("GetKeyboardLayoutName", Ptr, &KLID)
Return false
DllCall("ActivateKeyboardLayout", Ptr, priorHKL, UInt,0)
MsgBox, % StrGet(&KLID)
}
- 注意:我们实际上只能使用该行
DllCall("GetKeyboardLayoutName", Ptr, &KLID)
并手动更改语言,但实际上应该调用该行两次。
因此,按照俄罗斯的例子,0x4190419
是HKL
,00000419
是KLID
,并且04190419
毫无意义。
这里值得引用一下 Michael Kaplan,他是 Windows 国际组的一名开发人员。他发表了一篇题为“为什么键盘的HKL和KLID不一样?”。 他回答:
不管你信不信,事实上我经常会被问到这个问题。
人们看到这两个数字,发现它们的相似之处,然后开始假设它们是相同的
如果你通过 API 安装键盘,那么差异并不明显
LoadKeyboardLayout
[...] 在这种情况下,相同的 LCID [区域设置标识符] 总是被使用,并且如果键盘是具有KLID
像00000409
或00000407
这样的值的众多键盘之一,那么该HKL
值将与 相同KLID
,进一步使人们认为它们是相同的。然而,有两种情况它们可能并且将会有所不同:
- 任何时候,
KLID
值不仅仅是 LANGID——例如00010439
对于印地语传统键盘布局或0003041e
泰语 Pattachote(非 ShiftLock)键盘布局,HKL
将会有一个低位 DWORD 的高位字,其中包含不同的信息。- [...]
- 注意:请查看他的另外两篇与键盘布局相关的帖子,一些键盘术语以及恰如其名的我如何获取键盘的@!#$% 名称?。
好的,那么印地语怎么样?
不幸的是,事实证明所有这些信息仍然无济于事!具体来说:
- 调用
getKLIDfromHKL(0xf00c0439)
(针对传统印地语)会导致第一次调用时出现错误ActivateKeyboardLayout
,我不确定为什么。 - 呼叫
getKLIDfromHKL(0x4090439)
(印地语语音)输出00000409
,这是英语的 KLID。