从Windows中提取键盘布局

时间:2009-03-19 10:44:40

标签: c# winforms keyboard-layout

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

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

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

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

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

更新:关于此主题的相当不错的系列(我相信)here

5 个答案:

答案 0 :(得分:6)

Microsoft Keyboard Layout Creator可以加载系统键盘并将其导出为.klc files。由于它是用.NET编写的,因此您可以使用Reflector来查看它是如何做到的,并使用反射来驱动它。这是使用以下C#代码创建的zip file of .klc files for the 187 keyboards in Windows 8。请注意,我最初是为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中,设置“另存为”文件名,说明它是否已经配置了自定义键盘属性,然后模拟一个点击文件 - >保存菜单项。

答案 1 :(得分:2)

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

答案 2 :(得分:2)

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

在注册表中,您会看到:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts

其中每个分支都有一些像"Layout File"="KBDSP.dll"这样的值。根目录是

C:\Windows\System32

C:\Windows\SystemWOW64

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

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

enter image description here

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

答案 3 :(得分:2)

众所周知,MSKLC无法忠实地导入和复制Windows提供的所有.DLL文件的键盘布局,尤其是Windows 8及更高版本中的键盘布局。如果您无法从这些文件中提取任何有意义或有用的信息,那么知道这些文件在哪里也无济于事。 迈克尔·卡普兰(Michael Kaplan)在他的博客(他曾是MSKLC的开发者)上对此进行了记录,我发现您已链接到上面。

当MSKLC遇到它不了解的任何内容时,该部分将被删除。 使用MSKLC提取布局可用于大多数键盘,但是有几种-即切诺基键盘和日文和韩文键盘(仅举几例,我不确定还有多少种键盘)-提取的是布局不会准确或完全反映键盘的实际使用情况和功能。 切诺基键盘已链接MSKLC不支持的死键。而且远东的键盘具有MSKLC不知道的修饰键-这意味着缺少整个图层/移位状态!

Michael Kaplan提供了一些代码并解锁了MSLKC及其附带的软件的一些秘密,这些秘密可用于解决这些限制中的一些限制,但是它需要大量的手工工作-正是您要尝试的事情避免!另外,Michael的目标是创建具有MSKLC无法创建或理解的功能的键盘,但是这些功能确实可以在Windows中工作(这与OP试图实现的功能相反)。

我确定我的解决方案来不及使用OP,但是将来对类似情况的人可能会有所帮助。这是我发布此帖子的希望和理由。

到目前为止,我所做的只是解释其他答案还不够。即使是最好的键盘,也不会,也不会完全准确地重现Windows的所有本机键盘并将它们呈现为KLC源文件。这确实是不幸的,并且肯定不是作者的错,因为那是一段非常聪明的代码/脚本!幸运的是,脚本和源文件(其链接可能会或可能仍然无法使用)对于大多数Windows键盘以及MSKLC创建的任何自定义键盘都是有用且有效的。

具有MSKLC不支持的高级功能的键盘是由Windows DDK创建的,但是这些功能并未正式记录。尽管可以通过研究MSKLC随附的源文件来了解它们的潜力。

遗憾的是,我唯一能提供的解决方案是名为KbdEdit的第三方付费软件。我相信这是目前唯一可用的解决方案,实际上能够忠实地解码和重新创建Windows提供的任何键盘-尽管有一些高级功能甚至无法再现(例如,执行特殊本地语言的按键组合/热键)函数;例如:Ctrl + CapsLock激活KanaLock(日语修改器层)KbdEdit确实复制了带MSKLC的修改器层,如果不这样做,它不支持激活移位状态的替代方法的日语键盘带有假名锁定键,尽管如此,它可以让您将键盘上的键转换为假名键(也许是Scroll Lock?)。

幸运的是,所有这些不受支持的功能甚至都不适用于屏幕键盘。

KbdEdit是一个功能强大且令人赞叹的工具,值得我为此付出的每一分钱! (关于几乎所有其他付费软件,我都不会这么说……) 即使KbdEdit是第三方软件,也只需要创建键盘即可,而无需使用它们。它创建的所有键盘都可以在没有安装KbdEdit的任何Windows系统上本地运行。 它最多支持15个修改器状态和三个附加修改器键,其中之一是可切换的,如CapsLock。它还支持链式死键,并且可以重新映射大多数键盘上的任何键。

答案 4 :(得分:0)

请检查以下Windows API

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

检查MSDN here