В моем Java-коде я хотел бы имитировать невидимый щелчок мышью по определенным координатам экрана (x, y) при нажатии клавиши Q. Это должно быть незаметно для человеческого глаза: движение мыши ->
щелчок мыши ->
движение мыши назад.
Для этой цели я использую JNA 5.6.0 и jna-platform 5.6.0, которые используют собственные функции WinAPI. Я реализовал низкоуровневый хук клавиатуры внутри LowLevelKeyboardProc() {callback()}
, который перехватывает нажатия клавиш. Для имитации щелчка мыши я использую SendInput()
.
Я ожидал, что щелчок мышью должен быть сделан по определенным глобальным координатам экрана (x, y). В моем примере кода координаты 40, 40.
Но вместо ожидаемого результата я получил:
Лучший способ продемонстрировать это поведение, запустить приведенный ниже код, открыть Paint, выбрать инструмент «Кисть» и нажать кнопку «Q».
Вот как это выглядит:
Как видите, курсор не возвращается в исходное положение после первого нажатия кнопки Q. Следующие нажатия кнопки Q имеют большее расстояние между точкой щелчка и текущим положением мыши, и это расстояние отличается после первого нажатия кнопки 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 имеют большее расстояние между точкой щелчка и текущим положением мыши, и это расстояние отличается после первого нажатия кнопки Q.
Документация уже объясняет это:
Относительное движение мыши зависит от скорости мыши и пороговые значения для двух мышей. Пользователь устанавливает эти три значения с помощью ползунок «Скорость указателя» в свойствах мыши панели управления. лист. Вы можете получить и установить эти значения с помощью Функция SystemParametersInfo.
Система применяет два теста к указанному относительному движению мыши. Если указанное расстояние по оси x или y больше, чем первое пороговое значение мыши, а скорость мыши не равна нулю, система удваивает расстояние. Если указанное расстояние вдоль либо ось x или y больше, чем второе пороговое значение мыши, и скорость мыши равна двум, система удваивает расстояние, которое в результате применения первого порогового теста. Таким образом, возможно чтобы система умножала заданное относительное перемещение мыши вдоль оси x или y до четырех раз.
Вы можете использовать абсолютные координаты для завершения проекта.
Если указано значение MOUSEEVENTF_ABSOLUTE, dx и dy содержат нормализованные абсолютные координаты от 0 до 65 535. Событие процедура отображает эти координаты на поверхность дисплея. Координация (0,0) отображается в верхнем левом углу поверхности дисплея; координата (65535,65535) отображается в правом нижнем углу. В В многомониторной системе координаты сопоставляются с основным монитором.
Часть кода:
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. Слишком много зависимых хуков повлияет на производительность операции.
Как видите, курсор не возвращается в исходное положение после первого нажатия кнопки Q.
Вы можете сохранить начальные координаты. Затем возвращайтесь в исходное положение, когда это необходимо.
Демонстрация гифки:
Обновлено:
Чтобы еще больше помочь вам решить проблему, я продемонстрирую вам некоторые операции.
Мы используем GetCursorPos
, чтобы получить координаты ABC.
А(267 337) Б(508 334) С(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.
Затем мы можем вычислить координату D на основе расстояния между A и D, что может помочь вам решить проблему перемещения двух соседних координат.
Кроме того, мы можем точно настроить расстояние в соответствии с реальной ситуацией.
Добавьте следующий код,
mouseAction(267-87, 337-65, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE); // D(267-87, 337-65)
@Gepard, я проверил твою тему. Обратите внимание: полученные координаты x и y необходимо умножить на 65536.0f / GetSystemMetrics(SM_CXSCREEN/ SM_CYSCREEN)
. Я объяснил причину в ответе. Кроме того, я обновлю ответ, чтобы помочь решить некоторые проблемы.
Большое спасибо, теперь я понял, как это работает :)
Спасибо за Ваш ответ. К сожалению, мне не удалось реализовать то поведение, которое мне было нужно, мой клик производится по неправильным координатам и не возвращается в исходное положение. Но мне все это нужно для реализации клика по координатам в игре. Если вам не сложно, посмотрите мой этот вопрос. Возможно, вы можете что-то предложить. Спасибо. stackoverflow.com/questions/65316317/…