040 模擬鍵盤滑鼠

Post date: 2015/7/19 上午 02:44:11

实现一个鼠标或者键盘模拟器,可以完成很多功能,比如做一个简单的游戏外挂^_^。

通常,模拟键盘鼠标事件有两种方法: 1.keybd_event, mouse_event 2.SendMessage, PostMessage 后者更强大,指定hwnd后可以后台发送对应的鼠标键盘消息,而后者只能够发送前台信息,也就是只能向前端窗口发送消息。

下面是简单的用法举例: 方法1:使用keybd_event, mouse_event SetCursorPos(x, y);

mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);

mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);

keybd_event(k, 0, 0, 0);

keybd_event(k, 0, KEYEVENTF_KEYUP, 0);

方法2:使用SendMessage, PostMessage 向当前程序的ID为IDC_EDIT1的输入框输入字符: HWND tw2 = GetDlgItem(IDC_EDIT1)->m_hWnd;

::SendMessage(tw2, WM_CHAR, s[i], 0);

向ID为IDOK的按钮发送鼠标单击事件: HWND tw3 = GetDlgItem(IDOK)->m_hWnd;

::SendMessage(tw3, WM_LBUTTONDOWN, 0, 0);

::SendMessage(tw3, WM_LBUTTONUP, 0, 0);

向打开的记事本输入字符 wnd = ::FindWindow("notepad", NULL);

wnd = FindWindowEx(wnd, 0, "Edit", NULL);

::SendMessage(tw2, WM_CHAR, s[i], 0);

如果说你不知道名字叫Edit,那么有下面的方法: CWnd * pwnd = FindWindow("notepad", NULL);

CWnd * p2 = pwnd->GetTopWindow();

wnd = p2->m_hWnd;

::SendMessage(tw2, WM_CHAR, s[i], 0);

如果连notepad也不知道的话,你可以使用进程ID:遍历进程池得到想要的进程ID,然后找到指定ID的hwnd struct EnumParam {

HWND hMainWnd;

DWORD dwProcessID;

};

BOOL CALLBACK EnumWinProc(HWND hwnd, LPARAM lParam) {

DWORD dwID;

EnumParam* pep = (EnumParam*) lParam;

GetWindowThreadProcessId(hwnd, &dwID);

if (dwID == pep->dwProcessID) {

pep->hMainWnd = hwnd;

return 0;

} return TRUE;

} EnumParam ep;

STARTUPINFO si;

PROCESS_INFORMATION pi;

ep.hMainWnd = NULL;

memset(&si, 0, sizeof(si));

si.cb = sizeof(STARTUPINFO);

if (CreateProcess(NULL, "notepad.exe c:.txt", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {

CloseHandle(pi.hThread);

CloseHandle(pi.hProcess);

ep.dwProcessID = pi.dwProcessId;

while (!ep.hMainWnd) {

EnumWindows((WNDENUMPROC) EnumWinProc, (long) &ep);

//没有下面的一行,CPU的使用率会高居不下,同时其它的程序

//也会执行的很慢(包括程序启动的Notepad.exe)

if (ep.hMainWnd == NULL) Sleep(20);

}

//EnumWindows((WNDENUMPROC)EnumWinProc, (long)&ep);

}

//此时的 ep.hMainWnd 就是你要的NodePad.exe窗口的句柄

//接下来由hwnd得到CWND * wnd = ep.hMainWnd;

if (wnd == NULL) AfxMessageBox("Fasle");

CWnd * tc = FromHandle(wnd);

//获取

wnd = tc->GetTopWindow()->m_hWnd;

::SendMessage(tw2, WM_CHAR, s[i], 0);

目的

最近项目要求在Windows CE下模拟键盘输入,上网搜索了一下,发现有3个API可以用:SendMessage,keybd_event,PostKeybdMessage。

分析

1. SendMessage

(1) 模拟输入字符 - ANSI

// Input a 'a'

::SendMessage(hWnd, WM_CHAR, 'a', 0);

(2) 模拟输入字符 - Unicode

// Input a '我'

::SendMessage(hWnd, WM_CHAR, L'我', 0)

疑问:用SendMessage是直接向目标窗口发消息,很怀疑它是不是真的在“模拟”。

2. keybd_event

这个API的定义可以在这里找到:http://msdn.microsoft.com/en-us/library/aa453245.aspx

(1) 模拟输入字符 - ANSI

// Input a 'a'

::keybd_event(0x41, 0, 0, 0);

::keybd_event(0x41, 0, KEYEVENTF_KEYUP, 0);

(2) 模拟输入字符 - Unicode

keybd_event的定义说,第一个参数bVk的取值范围只能是[1, 254],而且Windows CE下的keybd_event并不支持KEYEVENTF_UNICODE,而且我也没办法试出用keybd_event输入Unicode字符的方法。

(3) 模拟输入命令

// Paste - Ctrl + V

::keybd_event(VK_LCONTROL, 0, 0, 0);

::keybd_event(0x56, 0, 0, 0);

::keybd_event(0x56, 0, KEYEVENTF_KEYUP, 0);

::keybd_event(VK_LCONTROL, 0, KEYEVENTF_KEYUP, 0);

3. PostKeybdMessage

这个API的定义可以在这里找到:http://msdn.microsoft.com/en-us/library/ms911936.aspx

(1) 模拟输入字符

PostKeybdMessage

LPWSTR lpszBuffer = L"我是iVincentFeng";

INT nBufferLength = ::wcslen(lpszBuffer);

UINT* lpStateFlags = new UINT[nBufferLength];

UINT* lpTextBuffer = new UINT[nBufferLength];

for (INT nIndex = 0; nIndex < nBufferLength; nIndex++)

{

lpStateFlags[nIndex] = KeyStateDownFlag;

lpTextBuffer[nIndex] = (UINT)lpszBuffer[nIndex];

}

::PostKeybdMessage(hWnd, 0, lpStateFlags[0], nLength, lpStateFlags, lpTextBuffer);

delete [] lpStateFlags;

delete [] lpTextBuffer;

复制代码
复制代码

总结

个人认为

1. 当需要模拟键盘输入命令时,比如Ctrl + V,选择keybd_event;

2. 当需要模拟键盘输入一串字符时,选择PostKeybdMessage;

3. 当需要模拟键盘输入单个字符时,选择keybd_event。

资料

1. Virtual-Key Codes: http://msdn.microsoft.com/en-us/library/ms927178.aspx