从Windows中提取键盘布局

好的,这是一个有点奇怪的问题。

我们有一个触摸屏应用程序(即没有键盘)。 当用户需要输入文本时,应用程序会显示虚拟键盘 – 手工构建在WinForms中。

为每种新语言手工制作这些东西都是猴子的工作。 我认为Windows必须将此键盘布局信息隐藏在某些dll中的某处。 无论如何都会从窗口获取此信息?

欢迎其他想法(我认为至少从xml文件生成的东西必须比在VS中手动执行更好)。

(注意:说了所有这些,我注意到有一个日文键盘,状态机和所有……,所以XML可能不够)

更新 :关于这个主题的相当不错的系列(我相信) 在这里

Microsoft Keyboard Layout Creator可以加载系统键盘并将其导出为.klc文件 。 由于它是用.NET编写的,因此您可以使用Reflector查看它是如何做到的,并使用reflection来驱动它。 这是使用以下C#代码创建的Windows 8中187键盘的.klc文件的zip文件 。 请注意,我最初是为Windows XP编写的,现在使用Windows 8和屏幕键盘,它真的很慢,似乎崩溃任务栏:/但是,它确实有效:)

using System; using System.Collections; using System.IO; using System.Reflection; class KeyboardExtractor { static Object InvokeNonPublicStaticMethod(Type t, String name, Object[] args) { return t.GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic) .Invoke(null, args); } static void InvokeNonPublicInstanceMethod(Object o, String name, Object[] args) { o.GetType().GetMethod(name, BindingFlags.Instance | BindingFlags.NonPublic) .Invoke(o, args); } static Object GetNonPublicProperty(Object o, String propertyName) { return o.GetType().GetField(propertyName, BindingFlags.Instance | BindingFlags.NonPublic) .GetValue(o); } static void SetNonPublicField(Object o, String propertyName, Object v) { o.GetType().GetField(propertyName, BindingFlags.Instance | BindingFlags.NonPublic) .SetValue(o, v); } [STAThread] public static void Main() { System.Console.WriteLine("Keyboard Extractor..."); KeyboardExtractor ke = new KeyboardExtractor(); ke.extractAll(); System.Console.WriteLine("Done."); } Assembly msklcAssembly; Type utilitiesType; Type keyboardType; String baseDirectory; public KeyboardExtractor() { msklcAssembly = Assembly.LoadFile("C:\\Program Files\\Microsoft Keyboard Layout Creator 1.4\\MSKLC.exe"); utilitiesType = msklcAssembly.GetType("Microsoft.Globalization.Tools.KeyboardLayoutCreator.Utilities"); keyboardType = msklcAssembly.GetType("Microsoft.Globalization.Tools.KeyboardLayoutCreator.Keyboard"); baseDirectory = Directory.GetCurrentDirectory(); } public void extractAll() { DateTime startTime = DateTime.UtcNow; SortedList keyboards = (SortedList)InvokeNonPublicStaticMethod( utilitiesType, "KeyboardsOnMachine", new Object[] {false}); DateTime loopStartTime = DateTime.UtcNow; int i = 0; foreach (DictionaryEntry e in keyboards) { i += 1; Object k = e.Value; String name = (String)GetNonPublicProperty(k, "m_stLayoutName"); String layoutHexString = ((UInt32)GetNonPublicProperty(k, "m_hkl")) .ToString("X"); TimeSpan elapsed = DateTime.UtcNow - loopStartTime; Double ticksRemaining = ((Double)elapsed.Ticks * keyboards.Count) / i - elapsed.Ticks; TimeSpan remaining = new TimeSpan((Int64)ticksRemaining); String msgTimeRemaining = ""; if (i > 1) { // Trim milliseconds remaining = new TimeSpan(remaining.Hours, remaining.Minutes, remaining.Seconds); msgTimeRemaining = String.Format(", about {0} remaining", remaining); } System.Console.WriteLine( "Saving {0} {1}, keyboard {2} of {3}{4}", layoutHexString, name, i, keyboards.Count, msgTimeRemaining); SaveKeyboard(name, layoutHexString); } System.Console.WriteLine("{0} elapsed", DateTime.UtcNow - startTime); } private void SaveKeyboard(String name, String layoutHexString) { Object k = keyboardType.GetConstructors( BindingFlags.Instance | BindingFlags.NonPublic)[0] .Invoke(new Object[] { new String[] {"", layoutHexString}, false}); SetNonPublicField(k, "m_fSeenOrHeardAboutPropertiesDialog", true); SetNonPublicField(k, "m_stKeyboardTextFileName", String.Format("{0}\\{1} {2}.klc", baseDirectory, layoutHexString, name)); InvokeNonPublicInstanceMethod(k, "mnuFileSave_Click", new Object[] {new Object(), new EventArgs()}); ((IDisposable)k).Dispose(); } } 

基本上,它获取系统上所有键盘的列表,然后为每个键盘加载它在MSKLC中,设置“另存为”文件名,关于它是否已经配置了自定义键盘属性,然后模拟点击文件 – >保存菜单项。

你为什么不使用屏幕键盘(osk.exe)? 看起来你重新发明了轮子。 而不是最简单的!

我知道这些DLL文件的路径在哪里:

在您的注册表中,您会看到:

 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts 

其中每个分支都有一些值,如"Layout File"="KBDSP.dll" 。 根目录是

 C:\Windows\System32 

 C:\Windows\SystemWOW64 

这些都是键盘布局文件所在。 例如, KBDUS.dll表示“键盘为美国”。

我试图用我的MSKLC自定义DLL替换DLL文件,我发现它在“语言” – “输入法” – “预览”中自动加载布局映射图像:

在此处输入图像描述

所以我们知道DLL中存在映射。

请检查以下Windows API

  [DllImport("user32.dll")] private static extern long LoadKeyboardLayout(string pwszKLID, uint Flags); 

在这里查看MSDN