簡體   English   中英

JNA / WinAPI。 模擬鼠標點擊移動鼠標 cursor 並且不會將其返回到開始 position

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

在我的 Java 代碼中,我想在按下 Q 鍵時模擬在某些屏幕坐標(x,y)上的不可見鼠標點擊。 它對人眼應該是不可見的:鼠標移動->鼠標單擊->鼠標向后移動。

為此,我正在使用 JNA 5.6.0 和 jna-platform 5.6.0,它們使用本機WinAPI函數。 我在LowLevelKeyboardProc() {callback()}中實現了低級鍵盤鈎子,它攔截擊鍵。 對於鼠標單擊模擬,我使用的是SendInput()

預計鼠標點擊應該在某些全局屏幕坐標(x,y)上完成。 在我的代碼示例中,坐標是 40、40。

但我得到的不是預期的結果:

  • 鼠標移動和鼠標單擊,但沒有將其返回到單擊之前 cursor 所在的開始 position 。
  • 鼠標通過當前鼠標 position 的坐標 40、40 而不是全局屏幕的坐標移動並單擊。

演示此行為的最佳方法是運行下面提供的代碼,打開 Paint,select 畫筆工具,然后按“Q”按鈕。

這是它的樣子:

在此處輸入圖像描述

如您所見,cursor 在第一次按下 Q 按鈕后不會回到開始 position。 下一次按下 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());
    }
}

我在某處的代碼中犯了錯誤。

誰能告訴我如何在全局屏幕的坐標上實現不可見的鼠標左鍵單擊?

下一次按下 Q 按鈕后,點擊點和當前鼠標 position 之間的距離會更大,並且這個距離在第一次按下 Q 按鈕后會有所不同。

文檔已經解釋了這一點:

相對鼠標運動受鼠標速度和兩鼠標閾值的影響。 用戶使用控制面板鼠標屬性表的指針速度 slider 設置這三個值。 您可以使用 SystemParametersInfo function 獲取和設置這些值。

系統對指定的相對鼠標移動應用兩個測試 如果沿 x 或 y 軸的指定距離大於第一個鼠標閾值,並且鼠標速度不為零,則系統將距離加倍。 如果沿 x 或 y 軸的指定距離大於第二個鼠標閾值,並且鼠標速度等於 2,則系統將應用第一個閾值測試產生的距離加倍。 因此,系統可以將指定的沿 x 或 y 軸的相對鼠標移動最多乘以四倍。

您可以使用絕對坐標來完成項目。

如果指定了 MOUSEEVENTF_ABSOLUTE 值,則 dx 和 dy 包含 0 到 65,535 之間的歸一化絕對坐標。 事件過程將這些坐標映射到顯示表面上。 坐標(0,0)映射到顯示表面的左上角; 坐標 (65535,65535) 映射到右下角。 在多顯示器系統中,坐標 map 到主顯示器。

部分代碼:

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);
}

代碼是 C++,我不熟悉 JAVA 代碼,但我認為您可以輕松更改為 Java。

補充:可以使用GetAsyncKeyState來檢測Q鍵是否被按下。 過多的依賴鈎子會影響操作性能。

如您所見,cursor 在第一次按下 Q 按鈕后不會回到開始 position。

您可以保存起始坐標。 然后在需要的時候回到起始position。

動圖演示:

1

更新:

為了進一步幫助您解決問題,我將演示一些操作供您參考。

在此處輸入圖像描述

我們使用GetCursorPos來獲取 ABC 的坐標。

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

然后將它們的坐標傳遞給輸入結構,參考如下代碼:

#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;
}

雖然是C++代碼,但基本邏輯與winapi相同。 按Q鍵,鼠標移動到A坐標,按W鍵移動到B坐標,按E鍵移動到C坐標。

2

然后我們可以根據A和D的距離計算出D的坐標,可以幫你解決移動兩個相鄰坐標的問題。

在此處輸入圖像描述

另外,我們可以根據實際情況微調距離。

添加以下代碼,

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