簡體   English   中英

JNA / WinAPI。 模擬鼠標點擊而不移動 cursor 不能正常工作

[英]JNA / WinAPI. Simulate mouse click without moving the cursor doesn't work correctly

編輯:對不起,但我不確定我的問題是否正確關閉。 我被建議這個線程,但它沒有回答我的問題。 我能夠模擬鼠標點擊,但正如我在問題中描述的那樣,它不能正常工作。


我仍在學習 JNA 並在我的Java應用程序(JNA 5.6.0 和 jna-platform 5.6.0)中使用它,但我希望熟悉C語言的人也能理解我,因為 JNA 使用的是 ZA82560B798Z012A 函數我的操作系統是 Windows 10。

我有的:

  • Swing 應用程序啟動魔獸爭霸III游戲,運行游戲的exe文件。
  • 攔截擊鍵LowLevelKeyboardProc()並調用click()方法的低級鍵盤鈎子,如下所述。
  • 應該模擬鼠標點擊游戲坐標window(庫存、技能和控制所在的位置)的邏輯,按下某些后(如下圖所示)。

在此處輸入圖像描述

問題是我無法正確執行鼠標點擊游戲 window 的坐標。

我要提前聲明,我不違反游戲許可協議的規則,並且僅出於個人目的使用舊版游戲,1.26。 另外,我在其他編程語言中也看到過類似的實現,但我想在 Java 中實現它。

下面我附上我嘗試過的 3 個選項,以及問題的描述:

1 . 使用User32.INSTANCE.SendMessage()

public void click(KeyBindComponent keyBindComponent) {
        final int WM_LBUTTONDOWN = 513;
        final int WM_LBUTTONUP = 514;
        final int MK_LBUTTON = 0x0001;
        Map<String, Integer> cords = getCords(keyBindComponent);
        if (!cords.isEmpty()) {
            int xCord = cords.get("width");
            int yCord = cords.get("height");
            LPARAM lParam = makeLParam(xCord, yCord);
            user32Library.SendMessage(warcraft3hWnd, WM_LBUTTONDOWN, new WPARAM(MK_LBUTTON), lParam);
            user32Library.SendMessage(warcraft3hWnd, WM_LBUTTONUP, new WPARAM(MK_LBUTTON), lParam);
            System.out.println("x = " + xCord + " y = " + yCord);
        }
    }

public static LPARAM makeLParam(int l, int h) {
        // note the high word bitmask must include L
        return new LPARAM((l & 0xffff) | (h & 0xffffL) << 16);
    }

預計會在測試坐標點(建築物上)上進行不可見的點擊。 問題是該區域被分配了。 我假設執行了以下順序:在當前鼠標 position中單擊鼠標,然后將 cursor 移動到單擊的坐標點 但我不知道為什么會這樣。

在此處輸入圖像描述

2.使用User32.INSTANCE.PostMessage()

public void click(KeyBindComponent keyBindComponent) {
        final int WM_LBUTTONDOWN = 513;
        final int WM_LBUTTONUP = 514;
        Map<String, Integer> cords = getCords(keyBindComponent);
        if (!cords.isEmpty()) {
            int xCord = cords.get("width");
            int yCord = cords.get("height");
            LPARAM lParam = makeLParam(xCord, yCord);
            user32Library.PostMessage(warcraft3hWnd, WM_LBUTTONDOWN, new WPARAM(0), lParam);
            user32Library.PostMessage(warcraft3hWnd, WM_LBUTTONUP, new WPARAM(0), lParam);
            System.out.println("x = " + xCord + " y = " + yCord);
        }
    }

public static LPARAM makeLParam(int l, int h) {
        // note the high word bitmask must include L
        return new LPARAM((l & 0xffff) | (h & 0xffffL) << 16);
    }

發生了同樣的情況,不是點擊坐標,而是選擇了區域,以及在SendMessage()的情況下,我可能不會重新附加圖片兩次。

3.使用User32.INSTANCE.SendInput()

public void click(KeyBindComponent keyBindComponent) {
        Map<String, Integer> cords = getCords(keyBindComponent);
        if (!cords.isEmpty()) {
            int xCord = cords.get("width");
            int yCord = cords.get("height");
            mouseMove(xCord, yCord);
            mouseClick();
            System.out.println("x = " + xCord + " y = " + yCord);
        }
    }

void mouseMove(int x, int y) {
        final int MOUSEEVENTF_LEFTUP = 0x0004;
        final int MOUSEEVENTF_ABSOLUTE = 0x8000;
        INPUT input = new INPUT();
        INPUT[] move = (INPUT[]) input.toArray(2);

        // Release the mouse before moving it
        move[0].type = new DWORD(INPUT.INPUT_MOUSE);
        move[0].input.setType("mi");
        move[0].input.mi.dwFlags = new DWORD(MOUSEEVENTF_LEFTUP);
        move[0].input.mi.dwExtraInfo = new BaseTSD.ULONG_PTR(0);
        move[0].input.mi.time = new DWORD(0);
        move[0].input.mi.mouseData = new DWORD(0);

        move[1].type = new DWORD(INPUT.INPUT_MOUSE);
        move[1].input.mi.dx = new LONG(x);
        move[1].input.mi.dy = new LONG(y);
        move[1].input.mi.mouseData = new DWORD(0);
        move[1].input.mi.dwFlags = new DWORD(MOUSEEVENTF_LEFTUP + MOUSEEVENTF_ABSOLUTE);

        user32Library.SendInput(new DWORD(2), move, move[0].size());
    }

void mouseClick() {
        final int MOUSEEVENTF_LEFTUP = 0x0004;
        final int MOUSEEVENTF_LEFTDOWN = 0x0002;
        INPUT input = new INPUT();
        INPUT[] click = (INPUT[]) input.toArray(2);

        click[0].type = new DWORD(INPUT.INPUT_MOUSE);
        click[0].input.setType("mi");
        click[0].input.mi.dwFlags = new DWORD(MOUSEEVENTF_LEFTDOWN);
        click[0].input.mi.dwExtraInfo = new BaseTSD.ULONG_PTR(0);
        click[0].input.mi.time = new DWORD(0);
        click[0].input.mi.mouseData = new DWORD(0);

        click[1].type = new DWORD(INPUT.INPUT_MOUSE);
        click[1].input.setType("mi");
        click[1].input.mi.dwFlags = new DWORD(MOUSEEVENTF_LEFTUP);
        click[1].input.mi.dwExtraInfo = new BaseTSD.ULONG_PTR(0);
        click[1].input.mi.time = new DWORD(0);
        click[1].input.mi.mouseData = new DWORD(0);

        user32Library.SendInput(new DWORD(2), click, click[0].size());
    }

在這種情況下,坐標點上根本沒有點擊。 相反,當按下某些鍵時,會在當前鼠標 position中單擊鼠標。

順便說一句,我也嘗試過使用 Java Robot ,但它對我不起作用。 不幸的是,鼠標 cursor 從起始 position 移動(消失)大約一毫秒到您需要單擊並返回起始 position 的點。

感謝您閱讀到最后,對於如此繁瑣的解釋,我深表歉意。

誰能告訴我我在代碼中犯了什么錯誤以及在哪里犯了錯誤? 由於在所有 3 個選項中,我都沒有達到預期的行為。

對於第三種情況,您沒有使用MOUSEEVENTF_MOVE標志來移動鼠標,因此鼠標實際上並沒有移動。 而且還根據文件

如果指定MOUSEEVENTF_ABSOLUTE值,則dxdy包含 0 到 65,535 之間的歸一化絕對坐標

void mouseMove(int x, int y) {
    INPUT move[2] = {};
    
    DWORD fScreenWidth = ::GetSystemMetrics(SM_CXSCREEN);
    DWORD fScreenHeight = ::GetSystemMetrics(SM_CYSCREEN);
    move[0].type = move[1].type = INPUT_MOUSE;
    move[0].mi.dwFlags = MOUSEEVENTF_LEFTUP;// Release the mouse before moving it
    move[1].mi.dx = MulDiv(x, 65535, fScreenWidth);
    move[1].mi.dy = MulDiv(y, 65535, fScreenHeight);
    move[1].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;

    SendInput(2, move, sizeof(INPUT));
}

然后使用MOUSEEVENTF_LEFTDOWNMOUSEEVENTF_LEFTUP單擊當前位置。

或者可以直接將鼠標移動合並到點擊事件中:

void mouseMoveClick(int x, int y) {
    INPUT click[3] = {};

    click[0].type = INPUT_MOUSE;
    click[0].mi.dwFlags = MOUSEEVENTF_LEFTUP;// Release the mouse before moving it

    DWORD fScreenWidth = ::GetSystemMetrics(SM_CXSCREEN);
    DWORD fScreenHeight = ::GetSystemMetrics(SM_CYSCREEN);
    click[1].type = INPUT_MOUSE;
    click[1].mi.dx = click[2].mi.dx= MulDiv(x, 65535, fScreenWidth);
    click[1].mi.dy = click[2].mi.dy= MulDiv(y, 65535, fScreenHeight);
    click[1].mi.dwFlags = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;

    click[2].type = INPUT_MOUSE;
    click[2].mi.dwFlags = MOUSEEVENTF_LEFTUP | MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;

    SendInput(3, click, sizeof(INPUT));
}

如果想鼠標點擊后移動回原來的position,可以在移動前用GetCursorPos記錄當前的position。 然后使用 mouseMove 事件或更簡單的SetCursorPos返回到 position。

void click(int xCord, int yCord) {
    //mouseMove(xCord, yCord);
    POINT p = {};
    GetCursorPos(&p);
    mouseMoveClick(xCord, yCord);
    SetCursorPos(p.x, p.y);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM