머리말:
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 시뮬레이션 ( 1 및 2 참조 )
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