JNA / WinAPI。在不移动光标的情况下模拟鼠标单击无法正常工作
JNA / WinAPI. Simulate mouse click without moving the cursor doesn't work correctly
编辑:抱歉,我不确定我的问题是否已正确关闭。我被建议这个 thread 但它没有回答我的问题。我能够模拟鼠标点击,但它无法正常工作,正如我在问题中所描述的那样。
我仍在学习 JNA 并在我的 Java 应用程序中使用它(JNA 5.6.0 和 jna-platform 5.6.0) 但我希望熟悉 C 语言的人也能理解我,因为 JNA 使用的是 WinAPI 函数。我的 OS 是 Windows 10.
我有:
- 启动魔兽争霸III游戏的Swing应用程序,运行游戏的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);
}
预计会在 test 坐标点(在建筑物上)进行不可见的点击。但是问题是该区域被分配了。我假设执行了以下序列:在С当前鼠标位置单击鼠标并将光标移动到点击坐标点。但是我不知道为什么会这样。
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 个选项中,我都没有达到预期的行为。
对于第3种情况,你没有使用MOUSEEVENTF_MOVE
标志来移动鼠标,所以鼠标实际上并没有移动。并且还根据 document:
If MOUSEEVENTF_ABSOLUTE
value is specified, dx and dy contain
normalized absolute coordinates between 0 and 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_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
to record the current position before moving. Then use mouseMove event or simpler SetCursorPos
到return到那个位置。
void click(int xCord, int yCord) {
//mouseMove(xCord, yCord);
POINT p = {};
GetCursorPos(&p);
mouseMoveClick(xCord, yCord);
SetCursorPos(p.x, p.y);
}
编辑:抱歉,我不确定我的问题是否已正确关闭。我被建议这个 thread 但它没有回答我的问题。我能够模拟鼠标点击,但它无法正常工作,正如我在问题中所描述的那样。
我仍在学习 JNA 并在我的 Java 应用程序中使用它(JNA 5.6.0 和 jna-platform 5.6.0) 但我希望熟悉 C 语言的人也能理解我,因为 JNA 使用的是 WinAPI 函数。我的 OS 是 Windows 10.
我有:
- 启动魔兽争霸III游戏的Swing应用程序,运行游戏的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);
}
预计会在 test 坐标点(在建筑物上)进行不可见的点击。但是问题是该区域被分配了。我假设执行了以下序列:在С当前鼠标位置单击鼠标并将光标移动到点击坐标点。但是我不知道为什么会这样。
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 个选项中,我都没有达到预期的行为。
对于第3种情况,你没有使用MOUSEEVENTF_MOVE
标志来移动鼠标,所以鼠标实际上并没有移动。并且还根据 document:
If
MOUSEEVENTF_ABSOLUTE
value is specified, dx and dy contain normalized absolute coordinates between 0 and 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_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
to record the current position before moving. Then use mouseMove event or simpler SetCursorPos
到return到那个位置。
void click(int xCord, int yCord) {
//mouseMove(xCord, yCord);
POINT p = {};
GetCursorPos(&p);
mouseMoveClick(xCord, yCord);
SetCursorPos(p.x, p.y);
}