jna/winapi在不移动光标的情况下模拟鼠标单击无法正常工作

wlsrxk51  于 2021-07-03  发布在  Java
关注(0)|答案(1)|浏览(731)

编辑:对不起,我不确定我的问题是否正确结案。有人建议我写这篇文章,但它不能回答我的问题。我能够模拟鼠标点击,但它不能正常工作,我在我的问题中所描述的。
我仍在学习jna并在java应用程序(jna 5.6.0和jna平台5.6.0)中使用它,但我希望熟悉c语言的人也能理解我,因为jna使用的是winapi函数。我的操作系统是Windows10。
我所拥有的:
启动魔兽争霸iii游戏的swing应用程序,运行游戏的exe文件。
拦截击键的低级键盘钩子 LowLevelKeyboardProc() 还有电话 click() 方法,如下所述。
在按下某些键(如下图所示)后,模拟鼠标点击游戏窗口坐标(库存、技能和控件所在位置)的逻辑。

问题是我无法实现正确执行鼠标点击游戏窗口的坐标。
我想提前说,我没有违反游戏许可协议的规定,我只想把它用于旧版游戏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);
}

预计会在测试坐标点(在建筑物上)上发出不可见的单击。但问题是,该地区被分配了。我假设执行了以下顺序:在с指定鼠标位置并将光标移动到с协调点击点。但我不知道为什么会这样。
![](https://i.stack.imgur.com/aycZW.jpg)
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());
}
在这种情况下,根本没有单击坐标点。相反,当按下某些键时,会在当前鼠标位置单击鼠标。
顺便说一句,我也尝试过使用java robot,但对我不起作用。不幸的是,鼠标光标从起始位置移动(消失)了大约一毫秒,移动到需要单击并返回起始位置的位置。
谢谢你把这篇文章读到最后,我很抱歉这么麻烦的解释。
有人能告诉我我在代码中犯了什么错误吗?因为在所有3个选项中,我都没有达到预期的行为。
nzrxty8p

nzrxty8p1#

对于第三种情况,您没有使用 MOUSEEVENTF_MOVE 旗子移动鼠标,所以鼠标实际上没有移动。根据文件:
如果 MOUSEEVENTF_ABSOLUTE 值,则dx和dy包含0和65535之间的标准化绝对坐标

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_LEFTDOWN 以及 MOUSEEVENTF_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));
}

如果要在鼠标单击后移回原始位置,可以使用 GetCursorPos 移动前记录当前位置。然后使用mousemove事件或更简单的方法 SetCursorPos 回到原来的位置。

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

相关问题