winforms 如何区分物理鼠标点击和代码鼠标点击?

gev0vcfq  于 2022-12-14  发布在  其他
关注(0)|答案(2)|浏览(290)

我现在正在写一个自动点击器。我有一个非常困难的问题要解决,在我的知识水平。我有低水平的挂钩,以检测鼠标键下和键上。

private bool LeftButtonStatus;    
private void AutoClicker_Load(object sender, EventArgs e)
{      
    core.mouseHook.LeftButtonDown += new MouseHook.MouseHookCallback(mouseHook_LeftKeyDown);
    core.mouseHook.LeftButtonUp += new MouseHook.MouseHookCallback(mouseHook_LeftKeyUp);
}
private void mouseHook_LeftKeyDown(MouseHook.MSLLHOOKSTRUCT ma)
{
    LeftButtonStatus = true;
    StartClicking();
}
private void mouseHook_LeftKeyUp(KeyboardHook.VKeys key)
{
    LeftButtonStatus = false;
    StartClicking();    
}
private void StartClicking()
{
    if (LeftButtonStatus)
        LeftButtonTimer.Start();
    else
        LeftButtonTimer.Stop();      
}
private void LeftButtonTimer_Tick(object sender, EventArgs e)
{
    Core.LeftClick();
}

Core类中的click方法如下所示:

[DllImport("user32.dll")]
private static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
public static void LeftClick()
{
    mouse_event(((int)KeyStates.LeftDown) | ((int)KeyStates.LeftDown), 0, 0, 0, 0);
}

问题是,当我调用Core.LeftClick();时,我的钩子检测到它并停止计时器。
如何确保mouseHook_LeftKeyUpmouseHook_LeftKeyDown方法忽略Core.LeftClick();

zxlwwiss

zxlwwiss1#

你可以在你自己的代码中使用一个标志,在LeftClick()中设置ignore = true,在钩子方法中检查if (ignore == true),但问题是你会面临一个竞争条件,在这个条件下,用户生成的点击可能会在设置为true和钩子运行之间被处理,这将导致用户生成的点击被忽略。
更好的方法是使用mouse_eventdwExtraInfo参数。The documentation将其描述为:
与鼠标事件关联的附加值。
这似乎是一个伟大的方式发送一些旗帜沿着在事件本身。
它本来是指向内存中某个位置的指针,用于存放一些数据,但是I found an example将其设置为某个任意的整数值(在他们的例子中为111),并且它可能会工作。
它看起来像这样:
第一个
您的mouseHook_LeftKeyUp方法签名不同,但这是正确的吗?如果您也可以更改它以使用MSLLHOOKSTRUCT,那么如果需要,您也可以在那里做同样的事情。
DllImport属性中,dwExtraInfo的类型从技术上讲应该是IntPtr,但在本例中这并不重要。

aamkag61

aamkag612#

  • 此答案整合并整合了其他SO帖子herehere中的信息和观点。*

您的问题是如何区分实际的鼠标点击和虚拟的鼠标点击。我发现在您的代码中使用了鼠标挂钩,并且“关闭循环”的一种方法是根据Gabriel Luci的出色建议检查回调中的dwExtraInfo标志。但我开始做的是找到一种线程安全的方法,它不会“我不依赖钩子来检测自动点击,所以我最终放弃了鼠标钩子来进行测试。我尝试了几种方法,但我发现,根据我的经验,最可靠的方法是使用线程同步对象设置一个约100毫秒的监视计时器(如易于使用的SemaphoreSlim)。该信号量可由最终使用点击的任何目标进行测试,以通过对信号量调用Wait(0)并查看bool返回值来确定WDT是否已过期。
在两个测试的第一个中,我检查了AutoClick按钮并让它运行。正如预期的那样,物理点击显示为黑色,自动点击显示为蓝色。指示灯都如预期的那样亮起来。

对于autoClick方法,我使用了SendInput,因为mouse_event已经过时了(请参阅Hans Passant对此post的评论。

public void autoClick(Control control)
{
    autoClick(new Point 
    { 
        X =  control.Location.X + control.Width / 2,
        Y =  control.Location.Y + control.Height / 2,
    });
}
public void autoClick(Point clientPoint)
{
    Cursor.Position = PointToScreen(clientPoint);

    var inputMouseDown = new INPUT { Type = Win32Consts.INPUT_MOUSE };
    inputMouseDown.Data.Mouse.Flags = (uint)MOUSEEVENTF.LEFTDOWN;
    // Go ahead and decorate with a flag as Gabriel Luci suggests.
    inputMouseDown.Data.Mouse.ExtraInfo = (IntPtr)MOUSEEXTRA.AutoClick;

    var inputMouseUp = new INPUT { Type = Win32Consts.INPUT_MOUSE };
    inputMouseUp.Data.Mouse.Flags = (uint)MOUSEEVENTF.LEFTUP;

    var inputs = new INPUT[]
    {
        inputMouseDown,
        inputMouseUp,
    };
    if (0 == SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT))))
    {
        Debug.Assert(false, "Error: SendInput has failed.");
    }
}
[Flags]
enum MOUSEEXTRA : uint{ AutoClick = 0x40000000, }

Auto Click 5复选框的处理程序定位并单击鼠标以点亮5个“indicator”复选框。

const int SETTLE = 100;
    SemaphoreSlim _sslimAutoClick = new SemaphoreSlim(1, 1);
    /// <summary>
    /// Responds to the button by auto-clicking 5 times.
    /// </summary>
    private async void onAutoClick(object sender, EventArgs e)
    {
        if (checkBoxAutoClick.Checked)
        {
            checkBoxAutoClick.Enabled = false;
            foreach (var indicator in indicators)
            {
                await _sslimAutoClick.WaitAsync();
                autoClick(indicator);
                // Don't await here. It's for the benefit of clients.
                Task
                    .Delay(SETTLE)
                    .GetAwaiter()
                    .OnCompleted(() =>_sslimAutoClick.Release());

                // Interval between auto clicks.
                await Task.Delay(TimeSpan.FromSeconds(2));
            }
            checkBoxAutoClick.Enabled = true;
            checkBoxAutoClick.Checked = false;
        }
    }

作为一个更严格的测试,我重复了这一点,但当5X自动点击运行时,我做了几次手动点击,以确保它们穿插并以黑色打印。再次,它按预期工作。

如果你想浏览完整的代码或尝试它,full sample code在GitHub上。

相关问题