在鼠标单击期间防止键盘事件(未释放)

Posted

技术标签:

【中文标题】在鼠标单击期间防止键盘事件(未释放)【英文标题】:Prevent Keyboard events during mouse Click (not released) 【发布时间】:2013-12-29 21:53:33 【问题描述】:

我实际上正在处理后台处理(在新创建的线程中,而不是守护进程中),它允许在特殊击键时绑定多个操作。

我的特殊按键是:鼠标左键单击,在释放单击之前,按下键盘上的某个键,然后在鼠标左键释放后执行一些动作。这部分仅适用于少数软件。

例如,在 html 输入中的 Google Chrome 或 notepad++ 上,如果我单击然后按下某个键,按下的字母/数字将照常显示,释放时执行操作(SMFL.Window.Keyboard 用于确定按下了哪个键)

我想阻止这种默认行为,记录“左键单击”期间按下的键,并继续使用记录的这些键进行处理。

我有 2 个想法:

1 - 使用来自 pInvoke 的 BlockInput

[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern bool BlockInput([In, MarshalAs(UnmanagedType.Bool)] bool fBlockIt);

但是这个方法总是返回false(零)。那是因为当前正在执行输入吗? (鼠标左键按下) 这可能是一个非常好的解决方案,但是:我无法测试它;它会阻止每个键盘事件吗? (如果我无法检索有关按下的键的任何信息,则它对我的应用毫无用处)

2 - 创建带焦点的不可见窗口

防止特定窗口上的键盘事件的另一种方法可能是打开一个不可见的窗口,将焦点设置在它上面,然后在释放鼠标按钮时关闭窗口。 按下鼠标按钮时,我无法成功将焦点设置在这个新打开的窗口上

一些代码:

while (true) // background process listening

  if (Mouse.IsButtonPressed(Mouse.Button.Left)) // keyboard/mouse event with SFML
    
         _focus = new focusManager(); // Start new thread to create new invisible window
         Thread thread = new Thread(new ThreadStart(_focus.preventFocus));
         thread.Start();
         _focus.forceFocus ();
    
  else
    
         _focus.giveFocus ();
    


 class focusManager
    
        [DllImport("user32.dll")]
        static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll")]
        static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);

        [DllImport("user32.dll")]
        static extern IntPtr SetFocus(IntPtr hWnd);

        [DllImport("kernel32.dll")]
        static extern IntPtr GetCurrentProcess();

        private IntPtr _focusedApp; // IntPtr id of "paused" app
        private IntPtr _currentId;  // IntPtr id of current app for invisible window focus
        private Form _f; // invisible window

        public void preventFocus ()
        
            // save IntPtr of current focused window
            _focusedApp = GetForegroundWindow();

            // debug print
            StringBuilder name = new StringBuilder(256);
            GetWindowText(_focusedApp, name, 256);
            Console.WriteLine("Steal Focus from " + name);

            openInvisibleWindow();
        

        private void openInvisibleWindow ()
        
           // get process ID to be able to set the focus on the invisible window
            _currentId = GetCurrentProcess();

            // create invisible window form
            _f = new Form();
            _f.FormBorderStyle = FormBorderStyle.FixedToolWindow;
            _f.ShowInTaskbar = false;
            _f.StartPosition = FormStartPosition.Manual;
            _f.Location = new System.Drawing.Point(100, 100); // visible for testing
            _f.Size = new System.Drawing.Size(100, 100);
            _f.Focus();
            _f.TopMost = true;

            Application.Run(_f);

        

        // set the focus on the new window
        public void forceFocus ()
        
             SetForegroundWindow(_currentId);

             // debug print
             StringBuilder name = new StringBuilder(256);
             GetWindowText(_currentId, name, 256);
             Console.WriteLine("have Focus on " + name);
        

        // set the focus on the "paused" app to launch keystroke action on it
        // close the invisible window
        public void giveFocus()
        
            SetForegroundWindow(_focusedApp);
            Application.Exit();
        
    

知道为什么 BlockInput 总是返回 False 吗? 知道为什么我不能将焦点放在这个新窗口上吗? 最后一种情况:防止特定应用程序上的键盘行为的任何其他想法(chrome、norepad++ ...、我的击键操作的目标)

非常感谢

【问题讨论】:

BlockInput() 需要 UAC 提升。您不能将焦点从刚刚获得输入事件的窗口中窃取。你需要一个键盘挂钩。 谢谢,我会做一些研究,我会试试这个hook 【参考方案1】:

使用键盘挂钩是找到的最佳工作解决方案。我使用了来自GitHub 的 Kb Hook,而不是 code-project 中的 article,它更易于使用且更紧凑。

为了阻止对当前窗口键的处理,回调必须返回 1 而不是CallNextHookEx windows API 函数的值。

在 Github 上找到的项目中,回调是:

private IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam);

并替换最后一行:

return InterceptKeys.CallNextHookEx(hookId, nCode, wParam, lParam);

通过

return (IntPtr) 1;

小心

CallNextHookEx 方法(此处在InterceptKeys 类中定义)为按键事件调用下一个挂钩。如果你替换调用,对按键事件的处理就不再处理了,这可能很危险,因为目标应用程序可能有自己的键盘 Hook。

【讨论】:

以上是关于在鼠标单击期间防止键盘事件(未释放)的主要内容,如果未能解决你的问题,请参考以下文章

鼠标与键盘操作事件

jQuery:jQuery的事件

第十三周总结

键盘事件

在 React 中的 onFocus 期间区分键盘和鼠标事件

JS 键盘回车和鼠标单击事件合并