Страницы

Поиск по вопросам

суббота, 4 января 2020 г.

Как создать бота на Java который работает с приложением (не Java) без фокуса?

#java #автоматизация #бот


Есть программа (бот) на Java.
Есть ДРУГОЕ приложение (Игра) в окне (1024х768).

Нужен бот который будет автоматический выполнять действия в игре.
В Java есть класс Robot:

import java.awt.Robot;

Robot r = new Robot();


который подходит для этого (Может эмулировать нажатия, клики мыши ), но этому классу
обязательно нужен фокус приложения.

Есть какой нибудь способ решения, который будет работать без фокуса?
(Подойдет любое решение, даже если придется писать эту часть кода на C/C++)
    


Ответы

Ответ 1



Cделать такое можно только с помощью JNI, то есть код придется писать на C/C++. Рекомендую посмотреть, как устроена внутри библиотека jnativehook, она уже умеет нативно ловить события. Вызвать C++ код из Java можно так: 1) создать Java-обертку для вызова кода: public class SendkeysTest { static { System.loadLibrary("sendkeys"); // sendkeys.dll (Windows) или sendkeys.so (Unix/GNU/Linux) } // Объявление native метода private native void sendKeys(); // Проверяем, что все работает public static void main(String[] args) { new Sendkeys().sendKeys(); } } 2) написать программу на С++ (sendkeys_impl.cpp) #include "sendkeys.h" #include using namespace std; void sendKeys () { //здесь код по отправке клавиш return; } 3) сделать заголовок для взаимодействия с Си кодом (sendkeys.h) #ifndef _SENDKEYS_H #define _SENDKEYS_H #ifdef __cplusplus extern "C" { #endif void sendKeys(); #ifdef __cplusplus } #endif #endif 4) оформить ее в виде DLL, в C файле (sendkeys.c) указав такую конструкцию: #include #include "sendkeys.h" #include "sendkeys_impl.cpp" JNIEXPORT void JNICALL Java_Sendkeys_sendKeys (JNIEnv *env, jobject thisObj) { sendKeys(); // вызываем функцию, ранее написанную на C++ return; } 5) собрать как-то так: g++ -Wl,--add-stdcall-alias -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -shared -o sendkeys.dll sendkeys.c sendkeys.cpp 6) можно запускать! java SendkeysTest или java -Djava.library.path=. SendkeysTest Более подробно все это описано в одной из множества инструкций по JNI . Дальше можно воспользоваться любым туториалом по отправке нажатий клавиш с помощью C++, или задать этот ворос на StackOverflow. Место, куда вписывать соответствующий код, у вас уже имеется. Если вам нужно посылать произвольные коды, то вам нужен либо SendInput() (он есть на windows 2k/xp и поэтому предпочтителен), или keybd_event() (работает в новых ОС). Еще есть события WM_SYSCOMMAND/WM_KEYDOWN/WM_KEYUP/WM_CHAR события для SendMessage. Например, вот так можно отправлять сообщения в Notepad с помощью упомянутого выше SendInput: int SendKeystrokesToNotepad( const TCHAR *const text ) { INPUT *keystroke; UINT i, character_count, keystrokes_to_send, keystrokes_sent; HWND notepad; assert( text != NULL ); //Получаем handle до окна Notepad. notepad = FindWindow( _T( "Notepad" ), NULL ); if( notepad == NULL ) return 0; //Перетаскиваем окно вперед if( !SetForegroundWindow( notepad ) ) return 0; //Заполняем массив клавиш, которые будем посылать character_count = _tcslen( text ); keystrokes_to_send = character_count * 2; keystroke = new INPUT[ keystrokes_to_send ]; for( i = 0; i < character_count; ++i ) { keystroke[ i * 2 ].type = INPUT_KEYBOARD; keystroke[ i * 2 ].ki.wVk = 0; keystroke[ i * 2 ].ki.wScan = text[ i ]; keystroke[ i * 2 ].ki.dwFlags = KEYEVENTF_UNICODE; keystroke[ i * 2 ].ki.time = 0; keystroke[ i * 2 ].ki.dwExtraInfo = GetMessageExtraInfo(); keystroke[ i * 2 + 1 ].type = INPUT_KEYBOARD; keystroke[ i * 2 + 1 ].ki.wVk = 0; keystroke[ i * 2 + 1 ].ki.wScan = text[ i ]; keystroke[ i * 2 + 1 ].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP; keystroke[ i * 2 + 1 ].ki.time = 0; keystroke[ i * 2 + 1 ].ki.dwExtraInfo = GetMessageExtraInfo(); } //Посылаем клавиши keystrokes_sent = SendInput( ( UINT )keystrokes_to_send, keystroke, sizeof( *keystroke ) ); delete [] keystroke; return keystrokes_sent == keystrokes_to_send; } Или с помощью SendMessage: int SendKeystrokesToNotepad( const TCHAR *const text ) { HWND notepad, edit; assert( text != NULL ); //Берем handle до окна Notepad notepad = FindWindow( _T( "Notepad" ), NULL ); if( notepad == NULL ) return 0; //Берем handle до контрола edit на этом окне edit = FindWindowEx( notepad, NULL, _T( "Edit" ), NULL ); if( edit == NULL ) return 0; //Собственно, посылаем SendMessage( edit, EM_REPLACESEL, ( WPARAM )TRUE, ( LPARAM )text ); return 1; } (Примеры взяты из вот этого обсуждения на основном StackOverflow) Нажатия кнопок мыши и ее движение тоже можно отправлять через SendInput: INPUT Input={0}; Input.type = INPUT_MOUSE; Input.mi.dx = (LONG)nx; Input.mi.dy = (LONG)ny; Input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP; SendInput(1,&Input,sizeof(INPUT)); Дальше уже ваша задача все это правильно склеить и адаптировать под вашу задачу, написание совершенно корректной посылалки клавиш на комбинации Java+C++ не влезет в разумный размер ответа.

Комментариев нет:

Отправить комментарий