全局捕获键按,但仅抑制当前应用和子窗口,无论焦点如何

时间:2013-10-28 15:02:36

标签: c# winforms winapi

我遇到了一个奇怪的问题。 我有一个WinForms应用程序打开另一个程序(计费系统模拟器),将其设置为子窗口,然后禁用它。这样工作正常,用户无法将任何键发送到该子窗口,并且winforms应用程序执行其操作,将命令发送到子窗口。

然而,已经发现推动 shift 控制,即使winforms应用程序没有焦点,也会导致错误计费系统模拟器,因为它们不是有效密钥。在winforms应用程序运行时,用户已经开始不使用 shift control 键,但这显然不是一个实用的解决方案。

我的尝试解决方案是:

  1. 按下这些键时要捕获的全局键盘钩。
  2. 覆盖winforms应用程序中的OnKeyDown以停止这些密钥。
  3. 但是,当winforms应用程序未处于焦点时,仍然无法解决发送到子窗口的 shift alt 键的问题。我可以在winforms应用程序运行时全局停止 shift alt ,但我认为这不是有效的。所以我需要以某种方式在全局钩子中停止winforms应用程序及其子项的keypress,但允许全局。任何想法/想法?

    这是my code

2 个答案:

答案 0 :(得分:2)

我认为你的情景没有一个好的答案...... = \

您可以尝试以下 hack 。如果它们关闭,它将“释放”Control / Shift,然后你发送信息:

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
    public static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int extraInfo);

    [DllImport("user32.dll")]
    static extern short MapVirtualKey(int wCode, int wMapType);

    private void button1_Click(object sender, EventArgs e)
    {
        if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift)
        {
            keybd_event((int)Keys.ShiftKey, (byte)MapVirtualKey((int)Keys.ShiftKey, 0), 2, 0); // Shift Up
        }
        if ((Control.ModifierKeys & Keys.Control) == Keys.Control) 
        {
            keybd_event((int)Keys.ControlKey, (byte)MapVirtualKey((int)Keys.ControlKey, 0), 2, 0); // Control Up
        }

        // ... now try sending your message ...
    }

这显然不是万无一失。

答案 1 :(得分:0)

我看了globalKeyboardHook的唯一构造函数,看起来它只是为全局钩子设计的。您可以添加另一个重载以挂钩到当前正在运行的模块中,如下所示:

class globalKeyboardHook {  
  [DllImport("kernel32")]
  private static extern int GetCurrentThreadId();
  [DllImport("user32")]
  private static extern IntPtr SetWindowsHookEx(int hookType, KeyBoardProc proc, IntPtr moduleHandle, int threadId);
  [DllImport("user32")]
  private static extern int CallNextHookEx(IntPtr hHook, int nCode, IntPtr wParam, IntPtr lParam);

  public globalKeyboardHook(bool currentModuleOnly){
     if(currentModuleOnly){
       proc = KeyBoardCallback;
       //WH_KEYBOARD = 0x2
       hhook = SetWindowsHookEx(2, proc, IntPtr.Zero, GetCurrentThreadId());       
     } else hook();
  }
  public delegate int KeyBoardProc(int nCode, IntPtr wParam, IntPtr lParam);
  public int KeyBoardCallback(int nCode, IntPtr wParam, IntPtr lParam) {
        if (nCode >= 0) {
            Keys key = (Keys)wParam;
            var lp = lParam.ToInt64();
            //your own handling with the key
            if ((lp >> 31) == 0)//Key down 
            { 
              //your own code ...
            } else { //Key up
              //your own code ...
            }
        }
        return CallNextHookEx(hHook, nCode, wParam, lParam);
  }
  KeyBoardProc proc;
  //other code ...
}
//then use the new overload constructor instead of the parameterless constructor:
globalHook = new globalKeyboardHook(true);

注意:您可以根据我上面发布的内容(评论KeyDown)实施您自己的KeyUpyour own code ...事件。经过一些搜索后,我了解WH_KEYBOARD_LL仅支持全局挂钩,而WH_KEYBOARD仅适用于thread hook。那应该是你想要的而不是WH_KEYBOARD_LL

BTW,我怀疑在这种情况下可以使用您的应用程序可以注册/添加的IMessageFilter。它还支持PreFilterMessage方法,帮助您拦截application-level处的任何键和鼠标消息。您应该尝试搜索,这很容易理解。

相关问题