繁体   English   中英

从 windows 中提取键盘布局

[英]Extracting keyboard layouts from windows

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

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

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

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

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

更新:关于这个主题的很好的系列(我相信)在这里

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

我知道这些 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 中。

众所周知,MSKLC 无法忠实地导入和重现 Windows 提供的所有.DLL 文件的键盘布局,尤其是 Windows 8 及更高版本中的那些文件。 如果您无法从中提取任何有意义或有用的信息,那么知道这些文件在哪里也没有任何好处。 Michael Kaplan 在他的博客(他是 MSKLC 的开发人员)上记录了这一点,我看到你已经链接到上面。

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

Michael Kaplan 提供了一些代码并解开了 MSLKC 的一些秘密以及可用于绕过其中一些限制的随附软件,但它需要大量的手工操作——这正是你试图避免的,另外, Michael 的目标是创建具有 MSKLC 无法创建或理解的功能的键盘。 但是哪个在 Windows 中工作(这与 OP 试图完成的相反)。

我确信我的解决方案来得太晚,无法用于 OP,但也许它在未来对处于类似情况的人会有所帮助。 这就是我发布这个的希望和原因。

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

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

遗憾的是,我能提供的唯一解决方案是名为KbdEdit的第 3 方付费软件。 我相信它是目前唯一真正能够忠实地解码和重新创建 Windows 提供的键盘的可用解决方案——尽管有一些高级功能甚至无法重现(例如执行特殊母语的组合键/热键功能;例如:Ctrl+CapsLock 激活 KanaLock(日本修改器层)。KbdEdit 确实忠实地再现了 MSKLC 剥离的修改器层,它只是不支持这种激活转换 state 的替代方法,如果你不这样做有一个带有假名锁定键的日文键盘。虽然,它允许您将键盘上的键转换为假名键(也许是 Scroll Lock?)。

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

KbdEdit 是一个非常强大且令人惊叹的工具,我为它付出的每一分钱都物有所值,(这不是我对几乎任何其他付费软件所说的话......)即使 KbdEdit 是第 3 方软件,它只需要创建键盘。 不要使用它们。 它创建的所有键盘都可以在没有安装 KbdEdit 的任何 Windows 系统上本地工作,它支持多达 15 个修改状态和三个附加修改键。 一种是可切换的——比如 CapsLock,它还支持链式死键。 并重新映射大多数键盘上的任何键。

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

请检查以下 Windows API

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

在此处查看 MSDN

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM