简体   繁体   English

JNA / WinAPI。 模拟鼠标点击移动鼠标 cursor 并且不会将其返回到开始 position

[英]JNA / WinAPI. Simulating mouse click moves mouse cursor and doesn't return it back to the start position

In my Java code, I would like to simulate invisible mouse click on certain screen coordinates (x, y) when key Q is pressed.在我的 Java 代码中,我想在按下 Q 键时模拟在某些屏幕坐标(x,y)上的不可见鼠标点击。 It should be invisible for human eye: mouse move -> mouse click -> mouse move back.它对人眼应该是不可见的:鼠标移动->鼠标单击->鼠标向后移动。

I am using JNA 5.6.0 and jna-platform 5.6.0 for this purpose which use native WinAPI functions.为此,我正在使用 JNA 5.6.0 和 jna-platform 5.6.0,它们使用本机WinAPI函数。 I implemented Low level keyboard hook inside LowLevelKeyboardProc() {callback()} that intercepts keystrokes.我在LowLevelKeyboardProc() {callback()}中实现了低级键盘钩子,它拦截击键。 For mouse click simulation I am using SendInput() .对于鼠标单击模拟,我使用的是SendInput()

I was expected that mouse click should be done on certain global screen coordinates (x, y).预计鼠标点击应该在某些全局屏幕坐标(x,y)上完成。 In my code example, coordinates are 40, 40.在我的代码示例中,坐标是 40、40。

But instead of expected result I got:但我得到的不是预期的结果:

  • the mouse movement and mouse click but without returning it back to the start position where cursor was located before click.鼠标移动和鼠标单击,但没有将其返回到单击之前 cursor 所在的开始 position 。
  • mouse moves and makes a click by coordinates 40, 40 from current mouse position and not by the coordinates of the global screen.鼠标通过当前鼠标 position 的坐标 40、40 而不是全局屏幕的坐标移动并单击。

The best way to demonstate this behavior, run the code provided below, open Paint, select the brush tool and press 'Q' button.演示此行为的最佳方法是运行下面提供的代码,打开 Paint,select 画笔工具,然后按“Q”按钮。

This is how it looks like:这是它的样子:

在此处输入图像描述

As you may see cursor doesn't move back to the start position after first time pressing Q button.如您所见,cursor 在第一次按下 Q 按钮后不会回到开始 position。 The next presses of the Q button have more distance between clicked point and current mouse position and this distance differs after first pressing Q button.下一次按下 Q 按钮后,点击点和当前鼠标 position 之间的距离会更大,并且这个距离在第一次按下 Q 按钮后会有所不同。

import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.*;

import static com.sun.jna.platform.win32.WinUser.*;

public class TestExample {

    public static final int MOUSEEVENTF_MOVE = 1;
    public static final int MOUSEEVENTF_LEFTDOWN = 2;
    public static final int MOUSEEVENTF_LEFTUP = 4;

    private static WinUser.HHOOK hHook;
    static final User32 user32Library = User32.INSTANCE;
    static WinDef.HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle(null);

    public static void main(String[] args) {
        keyReMapOn();
    }

    public static void keyReMapOn() {
        WinUser.LowLevelKeyboardProc keyboardHook = new WinUser.LowLevelKeyboardProc() {
            @Override
            public WinDef.LRESULT callback(int nCode, WinDef.WPARAM wParam, WinUser.KBDLLHOOKSTRUCT kbDllHookStruct) {
                if (nCode >= 0) {
                    if (wParam.intValue() == WM_KEYDOWN) {
                        if (kbDllHookStruct.vkCode == 81) {  // Q button
                            clickByCord(40, 40);            
                            return new WinDef.LRESULT(1);
                        }
                    }
                }
                Pointer ptr = kbDllHookStruct.getPointer();
                long peer = Pointer.nativeValue(ptr);
                return user32Library.CallNextHookEx(hHook, nCode, wParam, new WinDef.LPARAM(peer));
            }
        };

        hHook = user32Library.SetWindowsHookEx(WH_KEYBOARD_LL, keyboardHook, hMod, 0);

        int result;
        WinUser.MSG msg = new WinUser.MSG();
        while ((result = user32Library.GetMessage(msg, null, 0, 0)) != 0) {
            if (result == -1) {
                break;
            } else {
                user32Library.TranslateMessage(msg);
                user32Library.DispatchMessage(msg);
            }
        }
    }

    public static void clickByCord(int x, int y) {
        mouseMove(x, y);
        mouseLeftClick(x, y);
    }

    static void mouseMove(int x, int y) {
        mouseAction(x, y, MOUSEEVENTF_MOVE);
    }

    public static void mouseLeftClick(int x, int y) {
        mouseAction(x, y, MOUSEEVENTF_LEFTDOWN);
        mouseAction(x, y, MOUSEEVENTF_LEFTUP);
    }

    public static void mouseAction(int x, int y, int flags) {
        INPUT input = new INPUT();

        input.type = new DWORD(INPUT.INPUT_MOUSE);
        input.input.setType("mi");
        if (x != -1) {
            input.input.mi.dx = new LONG(x);
        }
        if (y != -1) {
            input.input.mi.dy = new LONG(y);
        }
        input.input.mi.time = new DWORD(0);
        input.input.mi.dwExtraInfo = new BaseTSD.ULONG_PTR(0);
        input.input.mi.dwFlags = new DWORD(flags);
        User32.INSTANCE.SendInput(new DWORD(1), new INPUT[]{input}, input.size());
    }
}

I made mistakes in the code somewhere.我在某处的代码中犯了错误。

Can anyone tell me how I can achieve invisible left mouse click on the coordinates of the global screen?谁能告诉我如何在全局屏幕的坐标上实现不可见的鼠标左键单击?

The next presses of the Q button have more distance between clicked point and current mouse position and this distance differs after first pressing Q button.下一次按下 Q 按钮后,点击点和当前鼠标 position 之间的距离会更大,并且这个距离在第一次按下 Q 按钮后会有所不同。

The documentation already explains this: 文档已经解释了这一点:

Relative mouse motion is subject to the effects of the mouse speed and the two-mouse threshold values.相对鼠标运动受鼠标速度和两鼠标阈值的影响。 A user sets these three values with the Pointer Speed slider of the Control Panel's Mouse Properties sheet.用户使用控制面板鼠标属性表的指针速度 slider 设置这三个值。 You can obtain and set these values using the SystemParametersInfo function.您可以使用 SystemParametersInfo function 获取和设置这些值。

The system applies two tests to the specified relative mouse movement .系统对指定的相对鼠标移动应用两个测试 If the specified distance along either the x or y axis is greater than the first mouse threshold value, and the mouse speed is not zero, the system doubles the distance.如果沿 x 或 y 轴的指定距离大于第一个鼠标阈值,并且鼠标速度不为零,则系统将距离加倍。 If the specified distance along either the x or y axis is greater than the second mouse threshold value, and the mouse speed is equal to two, the system doubles the distance that resulted from applying the first threshold test.如果沿 x 或 y 轴的指定距离大于第二个鼠标阈值,并且鼠标速度等于 2,则系统将应用第一个阈值测试产生的距离加倍。 It is thus possible for the system to multiply specified relative mouse movement along the x or y axis by up to four times.因此,系统可以将指定的沿 x 或 y 轴的相对鼠标移动最多乘以四倍。

You can use absolute coordinates to complete the project.您可以使用绝对坐标来完成项目。

If MOUSEEVENTF_ABSOLUTE value is specified, dx and dy contain normalized absolute coordinates between 0 and 65,535.如果指定了 MOUSEEVENTF_ABSOLUTE 值,则 dx 和 dy 包含 0 到 65,535 之间的归一化绝对坐标。 The event procedure maps these coordinates onto the display surface.事件过程将这些坐标映射到显示表面上。 Coordinate (0,0) maps onto the upper-left corner of the display surface;坐标(0,0)映射到显示表面的左上角; coordinate (65535,65535) maps onto the lower-right corner.坐标 (65535,65535) 映射到右下角。 In a multimonitor system, the coordinates map to the primary monitor.在多显示器系统中,坐标 map 到主显示器。

Part of the code:部分代码:

UINT mouseAction(int x, int y, int flags)
{
    INPUT input;
    POINT pos;
    GetCursorPos(&pos);

    input.type = INPUT_MOUSE;
    input.mi.dwFlags = flags;
    input.mi.time = NULL; 
    input.mi.mouseData = NULL;
    input.mi.dx = (pos.x + x)*(65536.0f / GetSystemMetrics(SM_CXSCREEN));
    input.mi.dy = (pos.y + y)*(65536.0f / GetSystemMetrics(SM_CYSCREEN));
    input.mi.dwExtraInfo = GetMessageExtraInfo();

    return SendInput(1, &input, sizeof(INPUT));
}

void mouseMove(int x, int y)
{
    mouseAction(x, y, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE);
}

The code is C++, I am not familiar with JAVA code, but I think it is easy enough for you to change to Java.代码是 C++,我不熟悉 JAVA 代码,但我认为您可以轻松更改为 Java。

Supplement: You can use GetAsyncKeyState to detect whether the Q key is pressed.补充:可以使用GetAsyncKeyState来检测Q键是否被按下。 Too many dependent hooks will affect operation performance.过多的依赖钩子会影响操作性能。

As you may see cursor doesn't move back to the start position after first time pressing Q button.如您所见,cursor 在第一次按下 Q 按钮后不会回到开始 position。

You can save the starting coordinates.您可以保存起始坐标。 Then return to the starting position when needed.然后在需要的时候回到起始position。

Gif demo:动图演示:

1

Updated:更新:

To further help you solve the problem, I will demonstrate some operations for your reference.为了进一步帮助您解决问题,我将演示一些操作供您参考。

在此处输入图像描述

We use GetCursorPos to get the coordinates of ABC.我们使用GetCursorPos来获取 ABC 的坐标。

A(267,337) B(508,334) C(714,329) A(267,337) B(508,334) C(714,329)

Then pass their coordinates to the input structure, refer to the following code:然后将它们的坐标传递给输入结构,参考如下代码:

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

UINT mouseAction(int x, int y, int flags)
{
    INPUT input;
    POINT pos;
    int x1 = GetSystemMetrics(SM_CXSCREEN);
    int y1 = GetSystemMetrics(SM_CYSCREEN);
    input.type = INPUT_MOUSE;
    input.mi.dwFlags = flags;
    input.mi.time = NULL;
    input.mi.mouseData = NULL;
    input.mi.dx = x* (65536.0f / GetSystemMetrics(SM_CXSCREEN));
    input.mi.dy = y* (65536.0f / GetSystemMetrics(SM_CYSCREEN));
    input.mi.dwExtraInfo = GetMessageExtraInfo();

    return SendInput(1, &input, sizeof(INPUT));
}


int main()
{           
    while(1)
    {
        if (GetAsyncKeyState(0x51) & 0x0001)
        {
            mouseAction(267, 337, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE);
        }
        if (GetAsyncKeyState(0x57) & 0x0001)
        {
            mouseAction(508, 334, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE);
        }
        if (GetAsyncKeyState(0x45) & 0x0001)
        {
            mouseAction(714, 329, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE);
    //        mouseAction(267-87, 337-65, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE);
        }
    }

    return 0;
}

Although it is C++ code, the basic logic is the same as that of winapi.虽然是C++代码,但基本逻辑与winapi相同。 Press the Q key, the mouse will move to the A coordinate, press the W key to move to the B coordinate, and press the E key to move to the C coordinate.按Q键,鼠标移动到A坐标,按W键移动到B坐标,按E键移动到C坐标。

2

Then we can calculate the coordinate of D based on the distance between A and D, which can help you solve the problem of moving two adjacent coordinates.然后我们可以根据A和D的距离计算出D的坐标,可以帮你解决移动两个相邻坐标的问题。

在此处输入图像描述

In addition, we can fine-tune the distance according to the actual situation.另外,我们可以根据实际情况微调距离。

Add the following code,添加以下代码,

mouseAction(267-87, 337-65, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE); // D(267-87, 337-65)

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM