更改键盘挂钩回调中的键盘布局

Posted

技术标签:

【中文标题】更改键盘挂钩回调中的键盘布局【英文标题】:Changing Keyboard Layout inside Keyboard Hook CallBack 【发布时间】:2013-05-25 21:17:40 【问题描述】:

我在更改键盘挂钩内的键盘布局时遇到问题。在这个简单的代码中,当按下'A'键时,需要很长时间才能更改语言,在更复杂的情况下,应用程序会做错事..

应用程序在托盘中工作,因此我使用了挂钩。 我的代码有什么问题? )) 或者,也许有不同的方法来改变键盘布局,这适用于钩子?感谢您的回答。

private static bool nextKey = false;

private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) 
    uint tpid = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
    ushort currentLayout = GetKeyboardLayout(tpid);

    if (nCode >= 0 && wParam == (IntPtr) WM_KEYDOWN) 
        if (nextKey) 
            Console.WriteLine("changing to english...");
            PostMessage(GetForegroundWindow(), 0x0050, 0, (int) LoadKeyboardLayout("00000409", 0x00000001));
            nextKey = false;
        

        int vkCode = Marshal.ReadInt32(lParam);

        if (vkCode == 0x41 && currentLayout == 0x409)  // if language is rus and 'A' pressed
            Console.WriteLine("changing to russian...");
            PostMessage(GetForegroundWindow(), 0x0050, 0, (int) LoadKeyboardLayout("00000419", 0x00000001));
            nextKey = true;
        
    
    return CallNextHookEx(_hookID, nCode, wParam, lParam);

【问题讨论】:

【参考方案1】:

在尝试修复此代码之前,我不得不问几个愚蠢的问题:

    为什么需要在挂钩内执行此操作? 您为什么需要这样做?您可以指定热键以在键盘控制面板中选择输入语言(区域和语言 → 键盘和语言 → 更改键盘 → 高级键设置)。并且每当您启用多种输入语言时,您的任务栏上都会放置一个图标。您无需编写自己的应用即可执行这些操作。

现在,专门查看您的代码,您正在做的是将WM_INPUTLANGCHANGEREQUEST 消息发布到前台窗口。但这条消息是一个通知。它通知程序用户请求更改输入语言。它并非旨在让程序向其他程序发出更改输入语言的请求。

如果程序想要改变自己的键盘布局,它会调用ActivateKeyboardLayout 函数。但是没有必要从 .NET 应用程序 p/invoke 这个函数。该框架已经将所有这些都包含在 InputLanguage 类中——强烈推荐。

除此之外,您未显示的代码,属于其他应用程序的代码中不可避免地存在其他问题。您向其发布WM_INPUTLANGCHANGEREQUEST 消息的前台窗口可以选择通过将消息传递给DefWindowProc 来接受更改,或者通过返回0 作为响应来拒绝更改。 If a broken application just 返回 0 for all those messages it does not explicitly process,它不会做正确的事情。或者,如果一个应用程序被编写为明确拒绝WM_INPUTLANGCHANGEREQUEST 请求,它就不会按照您的预期进行。等等。你无法控制这些事情。请记住,WM_INPUTLANGCHANGEREQUEST 只是一个请求。

就速度问题(“更改语言需要很长时间”)而言,第一次加载输入语言并不能保证是闪电般的快速操作。我看到使用正常机制在我的机器上延迟了大约半秒。通常不是大瓶颈;大多数人不会来回切换那么多次。如果您确实需要加快速度,请考虑缓存 LoadKeyboardLayout 函数的返回值。

【讨论】:

1.你好。我在应用程序中使用此代码从键盘捕获拉丁键并放置西里尔键。例如,如果我按“k”,我想打印“к”,或者如果我按“sh”,我想在我的屏幕上看到“ш”))要使它工作,我必须使用钩子。问题在于不是字符的键。例如,我是否按';',我想要';'要打印。 “简单”的方法是做同样的工作,就像我对字符所做的那样,我制作了一个字典,并使用了一个 SendKey。但我认为,改变布局是更好的方式。 2.在不改变布局的情况下,所有这些东西都运行良好。没有钩子,改变布局也很好。所以问题在于改变布局+钩子。 3. 我需要将 laout 更改为另一个应用程序,而不是用于运行此代码的应用程序,所以 InputLangue 帮不了我。 @Michael 不幸的是,你永远不会按照你用连字描述它的方式来完成这项工作。全局键盘挂钩不会一次检索多个字符,因此无法将sh 转换为ш。你永远不会看到sh,你会看到s,然后——一段时间后——h。除此之外,您无需更改输入语言即可执行您所描述的操作。使用钩子,您可以直接更改KBDLLHOOKSTRUCT 结构的内容。因此,您可以将最初输入的任何拉丁字符更改为您想要的任何西里尔字符。 1. “不可能将 sh 转换为ш” - 我已经做到了(s=с + h=х Backspase => ш)。问题仅在于更改布局。 2. 我可以用一种布局,但我也想用英文。所以另一种可能的解决方案是两种英文布局,但我认为我的解决方案更好(有两种英文布局,我需要在托盘中编写自己的语言栏..)

以上是关于更改键盘挂钩回调中的键盘布局的主要内容,如果未能解决你的问题,请参考以下文章

避免 android 软键盘调整布局大小。或回调隐藏视图

.NET 应用程序中未调用低级键盘挂钩

我无法在提升的 Windows 应用程序中全局挂钩键盘

win32 窗口阻止键盘布局更改

动态更改键盘布局

如果 Windows 任务栏具有当前焦点,则 LoadKeyboardLayout() 无法更改键盘布局