【问题标题】:Cannot detect global mouse down with MouseKeyHook无法使用 MouseKeyHook 检测全局鼠标按下
【发布时间】:2021-01-03 09:41:30
【问题描述】:

在过去的几个小时里,我一直在研究如何在全局范围内检测鼠标左键(不集中),并偶然发现了多个帖子提到使用我通过 NuGet 安装的globalmousekeyhook,我已经实现如下所示:

using Gma.System.MouseKeyHook;
using System;

namespace mouse_hook_test
{
    class Program
    {
        static private IKeyboardMouseEvents m_GlobalHook;

        static void Main(string[] args)
        {
            Subscribe();

            Console.ReadKey();
        }

        static public void Subscribe()
        {
            m_GlobalHook = Hook.GlobalEvents();

            m_GlobalHook.MouseDownExt += GlobalHookMouseDownExt;
        }

        static private void GlobalHookMouseDownExt(object sender, MouseEventExtArgs e)
        {
            Console.WriteLine("Mouse Click.");
        }
    }
}

当我运行它时,我的屏幕鼠标突然开始滞后到它无法使用的地步(我什至努力关闭命令提示符),我也尝试点击左右,但没有任何记录。我在做一些完全愚蠢的事情吗?我觉得我错过了一些非常明显的东西,因为没有其他人遇到这个问题,或者如果有另一种解决方案,我很乐意尝试。谢谢。

Visual Studio 2019, 视窗 10 专业版, 项目:.NET Framework 4.7.2,控制台应用程序,以调试模式运行

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    我实际上像往常一样在玩,寻找不同的方法,然后偶然发现Windows API function for getting states of keys (GetKeyStates)(包括鼠标),所以我导入了 user32 DLL 并启动了一个循环来检查鼠标左键状态(0x01) 每 50 毫秒(发现这是最好的时间)并将其放入一个线程中,这样它就不会阻塞主线程。

    using System;
    using System.Runtime.InteropServices;
    using System.Threading;
    
    namespace mouse_hook_test
    {
        class Program
        {
            static void Main(string[] args)
            {
                Thread thread = new Thread(detect);
                thread.Start();
            }
    
            // Import the DLL that contains the function and grab it.
            [DllImport("user32.dll")]
            public static extern short GetKeyState(int vKey);
    
            // A very simple implementation for tracking the state of the left mouse button.
            static void detect()
            {
                short oldState = GetKeyState(0x01);
    
                while (true)
                {
                    short newState = GetKeyState(0x01);                
    
                    if (oldState != newState)
                    {
                        oldState = newState;
    
                        if (newState < 0)
                        {
                            onMouseClick();
                        }
                    }
    
                    Thread.Sleep(50);
                }
            }
    
            static void onMouseClick()
            {
                Console.WriteLine("Clicked!");
            }
        }
    }
    

    可能有更好的解决方案(我找不到),所以我为什么要使用它。我最初基于对我的确切问题的回答,但它是用 Python 编写的,所以我将它转换为 C#。我厌倦了看到我的问题的唯一答案是“使用一些 3rd 方库”,无论出于何种原因,它都无法正常工作,并且还将内存使用量从 30mb 降低到 7mb :)。

    我还在 Windows 7 上对其进行了测试,效果非常好。

    【讨论】:

      【解决方案2】:

      原因是因为消息是由全局挂钩发布到线程的 Win32 消息队列中的。这与用于将消息传递到 Form 中的 WndProc 的队列相同。

      要接收消息,您必须使用Application.Run() 抽取消息队列。但是在控制台应用程序中,没有Application.Run()

      已经有varioussolutions为此提供了,但是我认为以下可能是最好的。

      using Gma.System.MouseKeyHook;
      using System;
      using System.Windows.Forms;
      
      namespace mouse_hook_test
      {
          class Program
          {
              static private IKeyboardMouseEvents m_GlobalHook;
      
              static void Main(string[] args)
              {
                  //Run pump on another background thread
                  //Task.Run feels like a bad idea without a pump/synchronisation context
                  var pumpThread = new Thread(PumpQueue){IsBackground = true};
                  pumpThread.Start();
      
                  Console.ReadKey();
                  Application.Exit();     // closes down the pump
              }
      
              [STAThread]      // I think this is necessary to ensure thread-safety
              static PumpQueue()
              {
                  Subscribe();
      
                  Application.Run();
              }
      
              static public void Subscribe()
              {
                  m_GlobalHook = Hook.GlobalEvents();
      
                  m_GlobalHook.MouseDownExt += GlobalHookMouseDownExt;
              }
      
              static private void GlobalHookMouseDownExt(object sender, MouseEventExtArgs e)
              {
                  Console.WriteLine("Mouse Click.");
              }
          }
      }
      

      【讨论】:

      • 同样的情况仍然发生,并且没有记录鼠标点击(强烈的鼠标滞后 + 没有记录)。
      • 已将Subscribe() 移至另一个线程。我认为它现在应该可以工作了
      • 这一次它开始记录点击,但延迟仍然存在,但现在它是一波又一波,当延迟消失 2 秒时,点击会被记录,但当它回来时,它们不再被记录。
      • 更具体地说,前几秒钟它工作正常,然后滞后,让我的朋友也测试它,它也一样。
      • 看起来ReadKey 可能存在与此有关的错误。改用ReadLine
      猜你喜欢
      • 2022-01-18
      • 1970-01-01
      • 2013-10-14
      • 1970-01-01
      • 2011-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-14
      相关资源
      最近更新 更多