【问题标题】:How can I delay return of CallNextHookEx or PostMessage to simulate delayed mouse events?如何延迟 CallNextHookEx 或 PostMessage 的返回以模拟延迟的鼠标事件?
【发布时间】:2020-06-06 12:10:19
【问题描述】:

我的鼠标偶尔会双击,现在我正在尝试构建一个工具来检测无效的 mouseup/mousdowns 并阻止它们传播。

双击实际上是鼠标失去联系然后再次接收触发(类似于短路但没有电)。如果我尝试拖动(例如通过选择文本),它会变得非常清晰:鼠标多次失去拖动。现在我试图忽略此类中间鼠标向上事件和以下鼠标向下事件,如果它连续发生以防止这种情况发生。

我设法使用 JNA 挂钩和阻止事件。但是,我的问题有点复杂:我只知道如果 mousedown 事件发生的时间少于 ms,我应该阻止 mouseup 事件,所以实际上我首先必须阻止 mouseup 事件,等待 ms,然后触发 mouseup事件(如果在此期间没有检测到 mousedown)或什么都不做(如果在容忍时间内发生 mousedown 事件,顺便说一句也应该被阻止)。但是,我只设法找出了如何广播 mouseup 事件(这总是触发点击 windows 按钮,所以我猜它不包括位置),但不知道如何正确触发我的实际 mouseup 事件一开始就被屏蔽了。

这是我的实现,它进行了适当的阻止,但之后错误地触发了点击(请注意,这不是实现,而只是用于测试模拟事件的简化版本)。

public class LowLevelMouseProcImpl implements LowLevelMouseProc {
    private User32 userInstance;
    private HHOOK hhk;
    private final HWND HWND_BROADCAST = new HWND(Pointer.createConstant(0xFFFF));
    public LowLevelMouseProcImpl(User32 userInstance, AtomicBoolean threadFinish) {
        this.userInstance = userInstance;
    }

    @Override
    public void setHook(HHOOK hook) {
        hhk = hook;
    }

    @Override
    public LRESULT callback(int nCode, WPARAM wParam, LPARAM lParam) {
        if (nCode < 0) {
            return userInstance.CallNextHookEx(hhk, nCode, wParam, lParam);
        }
        switch (wParam.intValue()) {
            case MouseHook.WM_LBUTTONUP:
                new Thread(() -> {

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    userInstance.PostMessage(HWND_BROADCAST, MouseHook.WM_LBUTTONUP, wParam, lParam);
                    //userInstance.CallNextHookEx(hhk, nCode, wParam, lParam);
                }).start();
                return new LRESULT(1);
                // break;
            case MouseHook.WM_RBUTTONUP:
                break;
            case MouseHook.WM_LBUTTONDOWN:
                new Thread(() -> {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    userInstance.PostMessage(HWND_BROADCAST, MouseHook.WM_LBUTTONDOWN, wParam, lParam);
                }).start();
                return new LRESULT(1);
                // break;
            case MouseHook.WM_RBUTTONDOWN:
                break;
            default:
                break;
        }
        return userInstance.CallNextHookEx(hhk, nCode, wParam, lParam);
    }
}

任何想法我可以如何正确发布消息?

【问题讨论】:

  • 无效双击是如何定义的?如果是正常的双击,应该怎么区分?
  • 与问题无关。因为我遇到了点击后自动变成双击的问题,最后我发现“开启ClickLock”应该是没有选中的。看到这个screensnap
  • ClickLock 与我的问题无关,因为我说这是硬件问题。我在第二段中描述了如何检测到无效的双击。也许我描述的有点不正确。我会更新描述。
  • 这个时间很难控制。因为当按下鼠标左键时,会出现多个鼠标丢失事件。这使得获得双击时间变得困难。也就是说,只要你按下鼠标左键,丢失鼠标的事件就会不断发生。我的建议是求鼠标丢失的平均时间,并在平均时间内防止mousedown事件和mouseup事件发生。当然,这个方法很粗糙,但是除了这个方法之外,我想不出其他可以获取你实际鼠标释放事件的方法了。
  • 是的,这就是我最初的意图。我的问题不是关于如何在理论上完成,而是如何在实践中完成。为了阻止一个事件并在以后(或不)触发它,我需要一种正确触发它的方法。这就是我的全部问题。

标签: java winapi callback mouseevent jna


【解决方案1】:

为了阻止一个事件并在以后(或不)触发它,我需要一种正确触发它的方法。这就是我的全部问题。

您可以使用WH_MOUSE_LL 来挂钩WM_LBUTTONDOWNWM_LBUTTONUP 事件。当您第一次按下鼠标左键时,会设置一个计时器以在特定时间内阻止WM_LBUTTONDOWNWM_LBUTTONUP 事件。一定时间后,自动发送鼠标左键释放事件。

这是一个c++代码示例,我对JNA不熟悉,但可以作为参考。

#include <Windows.h>
#include <iostream>

using namespace std;
#define time_ 2000    //average time

HHOOK mouseHook;
BOOL Fisrt_LBDOWN = TRUE;
BOOL LBUP = FALSE;
int ID = 0;

void CALLBACK f(HWND hwnd, UINT uMsg, UINT timerId, DWORD dwTime)
{   
    //After time_, automatically trigger WM_LBUTTONUP
    LBUP = TRUE;
    INPUT Input = { 0 };
    Input.type = INPUT_MOUSE;
    Input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
    SendInput(1, &Input, sizeof(INPUT));   //Simulate left mouse button release
}

LRESULT __stdcall MouseHookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode >= 0)
    {
        switch (wParam)
        {
        case WM_LBUTTONDOWN:
        {
             // The first time the left mouse button is pressed
             if (Fisrt_LBDOWN) 
             {
                //Trigger the timer to prevent WM_LBUTTONDOWN and WM_LBUTTONUP within the time_.
                ID = SetTimer(NULL, 0, time_, (TIMERPROC)&f); 
                Fisrt_LBDOWN = FALSE;
                return 0;
             }
             return 1;           
        }
        case WM_LBUTTONUP:
        {
            if (LBUP)
            {
                //Kill the timer and initialize all variables
                KillTimer(NULL, ID);
                Fisrt_LBDOWN = TRUE;
                LBUP = FALSE;
                return 0;
            }
            return 1;
        }

        }
    }
    return CallNextHookEx(mouseHook, nCode, wParam, lParam);
}

void SetHook()
{
    if (!(mouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookCallback, NULL, 0)))
    {
        cout << "Failed to install mousehook!" << endl;
    }
}

void ReleaseHook()
{
    UnhookWindowsHookEx(mouseHook);
}

int main()
{
    SetHook();
    MSG msg;

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    ReleaseHook();
    return msg.wParam;
}

【讨论】:

  • 我对 c++ 不是很熟悉,所以这些问题听起来可能很愚蠢:D f 从哪里得到它的参数? SendInput 来自哪里?它是Windows.h 的一部分吗?另外,如果我理解正确,它并没有真正做我打算做的事情,它只是阻止所有鼠标向上事件,直到计时器触发然后模拟一个,对吗?无论如何,实现细节不是我的问题,我只是在正确模拟事件方面有问题。
  • @GernotRaudner 正如我之前所说,您无法控制鼠标向上事件,因此您只能尝试在平均时间内阻止所有鼠标向上和向下事件。例如,当你按下鼠标左键并拖动时,鼠标会自动发送一个鼠标向上事件,这是你无法预料的。
  • @GernotRaudner TimerProcSendInputWindows.h 的一部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-25
  • 2016-08-01
  • 2018-09-22
  • 2018-06-13
  • 1970-01-01
  • 1970-01-01
  • 2018-02-22
相关资源
最近更新 更多