11

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

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

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

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

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

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

4

5 回答 5

7

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

于 2009-05-08T01:17:21.537 回答
2

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

于 2009-03-19T13:48:47.710 回答
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文件,我发现它会在“语言”-“输入法”-“预览”中自动加载布局映射图像:

在此处输入图像描述

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

于 2017-06-06T14:03:41.673 回答
2

众所周知,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 剥离的修改器层,如果您不支持,它只是不支持激活该转换状态的这种替代方法有一个带有假名锁定键的日文键盘。虽然,它允许您将键盘上的键转换为假名键(也许是 Scroll Lock?)。

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

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

于 2019-04-29T02:21:42.090 回答
0

请检查以下 Windows API

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

在此处查看MSDN

于 2009-03-19T10:48:27.487 回答