모든 키보드 및 마우스 시뮬레이션 방법의 원리와 실현

머리말:

DirectX 인터페이스를 사용하는 일부 게임 프로그램은 키보드 조작을 읽을 때 Windows 메시지 메커니즘을 우회하고 DirectInput을 사용합니다. 키보드 입력에 대한 반응 정도. 그러나 윈도우 메시지는 큐 형태이기 때문에 메시지가 전달 될 때 많은 지연이 발생하고 때로는 초당 12 개 이상의 메시지가 전달되기도합니다.이 속도는 게임의 요구 사항을 충족하지 않습니다. DirectInput은 윈도우 메시지를 우회하고 키보드 드라이버를 직접 처리하며 효율성은 크게 향상됩니다. 따라서 PostMessage 또는 keybd_event 사용 여부에 관계없이 이러한 기능은 더 높은 수준에 있기 때문에 이러한 프로그램에 대한 응답이 없습니다. 이러한 프로그램의 경우 하드웨어 이벤트를 시뮬레이션하기 위해 키보드 포트를 직접 읽고 쓰는 방법을 사용해야했습니다. 이 방법을 사용하여 키보드를 시뮬레이션하려면 키보드 프로그래밍에 대한 관련 지식을 이해해야합니다.

 

가장 간단한 시뮬레이션 작업부터 시작
하겠습니다 . 키보드와 마우스를 시뮬레이션하는 방법에는 여러 가지가 있습니다. 크게 메시지 시뮬레이션, API 시뮬레이션, 드라이버 시뮬레이션 으로 나눕니다 .
웹 페이지의 경우 JavaScript 시뮬레이션을 사용할 수도 있습니다. 이 튜토리얼의 범위

하나의 메시지 시뮬레이션

Windows 프로그래밍을 배우는 모든 사람은 Windows 프로그램이 창 메시지에 응답한다는 것을 알고 있으므로 우리는 스스로 메시지를 보냅니다. 과거에는 프로그램이 사람이 작동하고 응답하는 것으로 생각했습니다.

사용할 API 살펴보기

// 发送消息到指定窗口,不用等待消息处理就返回,参数和窗口过程里的一样

BOOL WINAPI PostMessage(

  _In_opt_ HWND   hWnd,

  _In_     UINT   Msg,

  _In_     WPARAM wParam,

  _In_     LPARAM lParam

);

// 获取窗口句柄,参数是窗口类名和窗口标题,其中一个可以传入NULL表示通配

HWND FindWindow(

LPCTSTR lpClassName,

LPCTSTR lpWindowName

);

// 用来获取子窗口句柄

HWND WINAPI FindWindowEx(

_In_opt_ HWND hwndParent,

_In_opt_ HWND hwndChildAfter,

_In_opt_ LPCTSTR lpszClass,

_In_opt_ LPCTSTR lpszWindow

);

메모장을 예로 들어

먼저 메모장의 창 클래스 이름을 알아야합니다.

메모장을 열고 VS2013을 열고 도구에서 spy ++를 찾습니다.

도구 모음에서 검색 창을 찾고 Finder 도구를 메모장 창으로 드래그 한 다음 메모장 창의 클래스 이름 "Notepad"를 가져옵니다.

같은 방법으로 편집 상자의 클래스 이름이 편집임을 알 수 있습니다.

편집 상자에서 A 누르기를 시뮬레이션하는 프로그램을 작성합니다.

HWND notepadWnd = FindWindow(_T("Notepad"), NULL); // 记事本窗口句柄

if (notepadWnd == NULL)

{

printf("没有找到记事本窗口\n");

return 0;

}

HWND editWnd = FindWindowEx(notepadWnd, NULL, _T("Edit"), NULL); // 编辑框窗口句柄


const BYTE vk = 'A'; // 虚拟键码

//UINT scanCode = MapVirtualKey(vk, MAPVK_VK_TO_VSC); // 扫描码

PostMessage(editWnd, WM_KEYDOWN, vk, 1 /*| scanCode << 16*/);

Sleep(100);

PostMessage(editWnd, WM_KEYUP, vk, 1 /*| scanCode << 16*/ | 1 << 30 | 1 << 31);

프로그램을 실행하여 메모장에 A가 더 있는지 확인하십시오.

 

마우스 오른쪽 버튼 클릭을 시뮬레이션하는 다른 프로그램 작성

사용 된 새 API


// 取坐标处窗口句柄

HWND WINAPI WindowFromPoint(

_In_ POINT Point

);

// 取鼠标坐标

BOOL WINAPI GetCursorPos(

_Out_ LPPOINT lpPoint

);

// 把屏幕坐标转为相对于窗口客户区的坐标

BOOL ScreenToClient(

_In_ HWND hWnd,

LPPOINT lpPoint

);

오른쪽 클릭을 시뮬레이션하는 프로그램

 
Sleep(3000); // 等待3秒把鼠标移到指定窗口


POINT pos; // 鼠标坐标

GetCursorPos(&pos);

HWND wnd = WindowFromPoint(pos); // 鼠标指向的窗口的句柄

ScreenToClient(wnd, &pos); // 把pos转成相对于窗口客户区的坐标

LPARAM lParam = MAKELPARAM(pos.x, pos.y);


PostMessage(wnd, WM_RBUTTONDOWN, 0, lParam);

Sleep(100);

PostMessage(wnd, WM_RBUTTONUP, 0, lParam);

실행 후 마우스를 메모장으로 이동하면 메뉴가 나타납니다.

 

입력을 시뮬레이션하기 위해 메시지를 보내는 방법의 장점은 창이 최소화되어 있어도 시뮬레이션 할 수 있다는 것입니다. 그러나 단점은 모든 프로그램이 창 메시지를 처리하지는 않는다는 것입니다. 예를 들어 대부분의 게임은 DInput을 사용하여 입력합니다.

두 개의 API 시뮬레이션 ( 12 참조 )

API 시뮬레이션은 keybd_event, mouse_event, SendInput과 같은 입력을 시뮬레이션하기 위해 Windows에서 제공하는 API를 사용하는 것이지만 Microsoft는 다른 두 가지 대신 SendInput을 사용하는 것이 좋습니다. 그런 다음 SendInput 사용 방법에 대해서만 이야기하겠습니다.

사용 된 API

UINT WINAPI SendInput(

_In_ UINT nInputs,

_In_ LPINPUT pInputs,

_In_ int cbSize

);


typedef struct tagINPUT {

  DWORD type;

  union {

    MOUSEINPUT    mi;

    KEYBDINPUT    ki;

    HARDWAREINPUT hi;

  };

} INPUT, *PINPUT;
이 API는 키보드 누르기, 마우스 이동, 마우스 클릭 등과 같은 이벤트를 시뮬레이션 할 수 있습니다. 매개 변수는 INPUT 구조의 수, INPUT 배열의 포인터 및 INPUT 구조의 크기입니다.

INPUT의 유형 값은 INPUT_MOUSE, INPUT_KEYBOARD, INPUT_HARDWARE이며 각각 mi, ki 및 hi 구조의 사용을 나타냅니다.

자세한 지침은 MSDN :
MOUSEINPUT KEYBDINPUT을 참조하십시오.

화면 중앙으로 마우스 이동을 시뮬레이션하고 오른쪽 클릭

INPUT input[3];

ZeroMemory(&input, sizeof(input));

// 鼠标移动到屏幕中间,也可以用SetCursorPos(x, y)

input[0].type = INPUT_MOUSE;

input[0].mi.dx = 65535 / 2; // 坐标取值范围是0-65535

input[0].mi.dy = 65535 / 2;

input[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;

// 点击鼠标右键

input[1].type = INPUT_MOUSE;

input[1].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;

input[2].type = INPUT_MOUSE;

input[2].mi.dwFlags = MOUSEEVENTF_RIGHTUP;


SendInput(_countof(input), input, sizeof(INPUT));

A 키 누르기 시뮬레이션 :

INPUT input[2];

ZeroMemory(&input, sizeof(input));

input[0].type = INPUT_KEYBOARD;

input[0].ki.wVk = 'A';

// 也可以不加这句但是对DInput输入的程序会没用

input[0].ki.wScan = MapVirtualKey(input[0].ki.wVk, MAPVK_VK_TO_VSC);

input[1].type = INPUT_KEYBOARD;

input[1].ki.wVk = input[0].ki.wVk;

input[1].ki.wScan = input[0].ki.wScan;

input[1].ki.dwFlags = KEYEVENTF_KEYUP;


SendInput(_countof(input), input, sizeof(INPUT));

고급 예를 들어 보겠습니다. Dongfang Huayingzuka의 Z 키 타격

오리엔탈 화잉 마운드에서 플릭을 보내려면 Z 버튼을 계속 눌러야하는데 매우 힘들 기 때문에 C 버튼을 길게 눌러 자동으로 플릭을 보내는 기능을 실현하고 싶습니다. )

이 프로그램은 MFC를 사용합니다. 이해가 안된다면 MFC 프로그래밍을 배우는 것이 좋습니다.

// 开启

void CC2ZDlg::OnBnClickedButton1()

{

m_enableButton.EnableWindow(FALSE);

m_disableButton.EnableWindow(TRUE);

SetTimer(1, 200, timerProc); // 每0.2s检测C键是否按下,并模拟Z键

}


// 关闭

void CC2ZDlg::OnBnClickedButton2()

{

m_enableButton.EnableWindow(TRUE);

m_disableButton.EnableWindow(FALSE);

KillTimer(1);

}


//定时模拟按下Z

void CALLBACK CC2ZDlg::timerProc(HWND hWnd, UINT nMsg, UINT nTimerid, DWORD dwTime)

{

if ((GetKeyState('C') & (1 << 15)) != 0) // C键按下

{

INPUT input;

ZeroMemory(&input, sizeof(input));

input.type = INPUT_KEYBOARD;

input.ki.wVk = 'Z';

input.ki.wScan = MapVirtualKey(input.ki.wVk, MAPVK_VK_TO_VSC);

SendInput(1, &input, sizeof(INPUT)); // 按下Z键

Sleep(100); // 可能东方是在处理逻辑时检测一下Z键是否按下才发弹幕,如果这时Z键刚好弹起就没有反应,所以要延迟一下

input.ki.dwFlags = KEYEVENTF_KEYUP;

SendInput(1, &input, sizeof(INPUT)); // 弹起Z键

}

}

완전한 소스 코드

이러한 방식으로 대부분의 게임 입력을 시뮬레이션 할 수 있지만 일부 게임은 보호되므로 드라이브 시뮬레이션이 사용됩니다.

세 드라이브 시뮬레이션

드라이브 시뮬레이션은 자체 드라이버를 작성하고 시스템 커널에서 I / O 포트를 작동하고 키보드 (일반적으로 8042 칩)에 연결된 집적 회로에 명령을 보내 버튼 누름 메시지를 생성하도록하는 것입니다. input is for all 프로그램은 실제 장치에서 전송되며 많은 보호를 우회
할 수 있습니다 (I / O 포트를 작동해야하는 경우 WinIo 라이브러리를 배울 수 있음 ).
하지만 이런 종류의 드라이버를 작성하지는 않습니다. _ ( : з」∠) _, x64 시스템에 드라이버를로드하려면 신뢰할 수있는 디지털 서명이 있어야합니다. 그렇지 않으면 더 번거로울 수 있으며 8042 칩에 대한 기본 지식도 알고 있습니다.
그래서 다음에서 작성한 라이브러리를 찾았습니다. Interception의
공식 웹 사이트 에서 Interception API를 시뮬레이션하는 Git 을 구동하기 위해 몇 사람이 있습니다
.

드라이버에는 디지털 서명이 있으며 XP부터 win10까지 플랫폼에서 테스트되었습니다.
입력을 가로 채고 수정할 수도 있지만 (CTRL + ALT + DELETE 포함) 여기서는 아날로그 입력에 대해서만 이야기하므로 직접 살펴 보겠습니다.
(아날로그 입력이 작동 포트가 아니라 SendInput의 커널 버전 인 것 같습니다.)

설치 방법 :

( 내 네트워크 디스크 로 이동할 수 있습니다. ) Interception.zip을 다운로드하고 압축을 풀고 install-interception.exe를 실행합니다.

환경 설정 : 대상 시스템이 64 비트 인 경우 먼저 구성 관리자에서 x64 구성을 추가해야합니다.

Interception \ library 디렉터리를 포함하여 프로젝트 속성에서 VC ++ 디렉터리를 찾습니다. 라이브러리 디렉터리는 대상 시스템이
링커 입력 찾기 위해 64 비트 또는 32 비트 + library \ x64 또는 library \ x86인지 여부를 기반으로합니다. 추가 종속성 및 차단 .lib
그런 다음 프로그램의 동일한 디렉토리에있는 library \ x64 또는 library \ x86에
interception.dll을 넣고 마지막으로 소스 코드에 #include <interception.h>

마우스가 화면 중앙으로 이동하고 마우스 오른쪽 버튼을 클릭하는 것을 시뮬레이션합니다.

 
InterceptionContext context = interception_create_context();


InterceptionMouseStroke mouseStroke[3];

ZeroMemory(mouseStroke, sizeof(mouseStroke));

// 鼠标移动到屏幕中间

mouseStroke[0].flags = INTERCEPTION_MOUSE_MOVE_ABSOLUTE;

mouseStroke[0].x = 65535 / 2; // 坐标取值范围是0-65535

mouseStroke[0].y = 65535 / 2;

// 点击鼠标右键

mouseStroke[1].state = INTERCEPTION_MOUSE_RIGHT_BUTTON_DOWN;

mouseStroke[2].state = INTERCEPTION_MOUSE_RIGHT_BUTTON_UP;

interception_send(context, INTERCEPTION_MOUSE(0), (InterceptionStroke*)mouseStroke, _countof(mouseStroke));


interception_destroy_context(context);

A 키 누르기 시뮬레이션 :

InterceptionContext context = interception_create_context();


InterceptionKeyStroke keyStroke[2];

ZeroMemory(keyStroke, sizeof(keyStroke));

keyStroke[0].code = MapVirtualKey('A', MAPVK_VK_TO_VSC);

keyStroke[0].state = INTERCEPTION_KEY_DOWN;

keyStroke[1].code = keyStroke[0].code;

keyStroke[1].state = INTERCEPTION_KEY_UP;

interception_send(context, INTERCEPTION_KEYBOARD(0), (InterceptionStroke*)keyStroke, _countof(keyStroke));


interception_destroy_context(context);

 

드라이브 시뮬레이션은 매우 강력하지만 더 번거롭고 일반적으로 사용되지 않습니다 _ (: з」∠) _

 

 

참조 : https://www.cnblogs.com/Jnshushi99/archive/2011/09/03/2164617.html

추천

출처blog.csdn.net/THMAIL/article/details/113812698