如何不使用GetAsyncKeyState检测左键按下?mouse_event干扰问题求助
解决方案:替代GetAsyncKeyState检测鼠标左键+避免模拟点击干扰状态检测
一、替代GetAsyncKeyState检测鼠标左键按下状态
可以通过全局鼠标钩子直接监听系统鼠标消息,维护物理左键的按下状态,完全不依赖GetAsyncKeyState,且不受模拟输入干扰。
实现代码
using System; using System.Runtime.InteropServices; public class MouseHook { private const int WH_MOUSE_LL = 14; private const int WM_LBUTTONDOWN = 0x0201; private const int WM_LBUTTONUP = 0x0202; private static LowLevelMouseProc _proc = HookCallback; private static IntPtr _hookID = IntPtr.Zero; public static bool IsLeftMouseDown { get; private set; } = false; private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr GetModuleHandle(string lpModuleName); public static void SetHook() { using (var curProcess = System.Diagnostics.Process.GetCurrentProcess()) using (var curModule = curProcess.MainModule) { _hookID = SetWindowsHookEx(WH_MOUSE_LL, _proc, GetModuleHandle(curModule.ModuleName), 0); } } public static void Unhook() { UnhookWindowsHookEx(_hookID); } private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { switch ((int)wParam) { case WM_LBUTTONDOWN: IsLeftMouseDown = true; break; case WM_LBUTTONUP: IsLeftMouseDown = false; break; } } return CallNextHookEx(_hookID, nCode, wParam, lParam); } }
使用方式
// 程序启动时注册钩子 MouseHook.SetHook(); // 循环中直接读取状态判断 void loop() { if (MouseHook.IsLeftMouseDown) { LeftClick(); } } // 程序退出时卸载钩子 MouseHook.Unhook();
二、解决模拟点击干扰状态检测的问题
问题根源
mouse_event是老旧API,模拟的鼠标输入会被系统当作真实硬件输入,发送WM_LBUTTONUP消息,导致GetAsyncKeyState误判左键状态。
解决方案:改用SendInput API
SendInput是Windows推荐的现代输入模拟API,可区分模拟输入与物理输入,不会干扰物理按键的状态检测。
替换后的LeftClick方法
[StructLayout(LayoutKind.Sequential)] struct INPUT { public int type; public MOUSEINPUT mi; } [StructLayout(LayoutKind.Sequential)] struct MOUSEINPUT { public int dx; public int dy; public uint mouseData; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; } [DllImport("user32.dll", SetLastError = true)] private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); public void LeftClick() { INPUT[] inputs = new INPUT[2]; // 模拟左键按下 inputs[0].type = 0; // 鼠标输入类型 inputs[0].mi.dwFlags = (uint)MouseEventFlags.MOUSEEVENTF_LEFTDOWN; // 模拟左键抬起 inputs[1].type = 0; inputs[1].mi.dwFlags = (uint)MouseEventFlags.MOUSEEVENTF_LEFTUP; SendInput(2, inputs, Marshal.SizeOf(typeof(INPUT))); }
完整优化代码示例
using System; using System.Runtime.InteropServices; using System.Threading; namespace MouseAutoClickDemo { public class MouseHook { private const int WH_MOUSE_LL = 14; private const int WM_LBUTTONDOWN = 0x0201; private const int WM_LBUTTONUP = 0x0202; private static LowLevelMouseProc _proc = HookCallback; private static IntPtr _hookID = IntPtr.Zero; public static bool IsLeftMouseDown { get; private set; } = false; private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr GetModuleHandle(string lpModuleName); public static void SetHook() { using (var curProcess = System.Diagnostics.Process.GetCurrentProcess()) using (var curModule = curProcess.MainModule) { _hookID = SetWindowsHookEx(WH_MOUSE_LL, _proc, GetModuleHandle(curModule.ModuleName), 0); } } public static void Unhook() { UnhookWindowsHookEx(_hookID); } private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { switch ((int)wParam) { case WM_LBUTTONDOWN: IsLeftMouseDown = true; break; case WM_LBUTTONUP: IsLeftMouseDown = false; break; } } return CallNextHookEx(_hookID, nCode, wParam, lParam); } } [Flags] enum MouseEventFlags : uint { MOUSEEVENTF_MOVE = 0x0001, MOUSEEVENTF_LEFTDOWN = 0x0002, MOUSEEVENTF_LEFTUP = 0x0004, MOUSEEVENTF_RIGHTDOWN = 0x0008, MOUSEEVENTF_RIGHTUP = 0x0010, MOUSEEVENTF_MIDDLEDOWN = 0x0020, MOUSEEVENTF_MIDDLEUP = 0x0040, MOUSEEVENTF_XDOWN = 0x0080, MOUSEEVENTF_XUP = 0x0100, MOUSEEVENTF_WHEEL = 0x0800, MOUSEEVENTF_VIRTUALDESK = 0x4000, MOUSEEVENTF_ABSOLUTE = 0x8000 } [StructLayout(LayoutKind.Sequential)] struct INPUT { public int type; public MOUSEINPUT mi; } [StructLayout(LayoutKind.Sequential)] struct MOUSEINPUT { public int dx; public int dy; public uint mouseData; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; } public class MouseController { [DllImport("user32.dll", SetLastError = true)] private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); public void LeftClick() { INPUT[] inputs = new INPUT[2]; inputs[0].type = 0; inputs[0].mi.dwFlags = (uint)MouseEventFlags.MOUSEEVENTF_LEFTDOWN; inputs[1].type = 0; inputs[1].mi.dwFlags = (uint)MouseEventFlags.MOUSEEVENTF_LEFTUP; SendInput(2, inputs, Marshal.SizeOf(typeof(INPUT))); } } class Program { static void Main(string[] args) { MouseHook.SetHook(); var controller = new MouseController(); Console.WriteLine("按住鼠标左键启动自动点击,按任意键退出"); while (!Console.KeyAvailable) { if (MouseHook.IsLeftMouseDown) { controller.LeftClick(); Thread.Sleep(50); // 控制点击间隔,避免操作过于频繁 } } MouseHook.Unhook(); } } }
核心说明
- 全局鼠标钩子直接捕获系统层面的鼠标消息,精准记录物理左键的真实状态,完全不受模拟输入影响。
SendInput替代mouse_event后,模拟的输入不会篡改系统对物理按键状态的判断,同时兼容性更强。
内容的提问来源于stack exchange,提问作者HHopter
相关产品推荐
相关产品推荐

