鼠标键盘操作也会转换成相应的系统消息,窗口进程收到鼠标或键盘消息后会进行相应的处理。可以通过SendMessage()和PostMessage()向指定的窗口进程发送消息,然后使用这两个函数发送鼠标和键盘相关的消息,进行鼠标和键盘的模拟操作。除了SendMessage()和PostMessage(),还可以通过两个专用函数keybd_event()和mouse_event()来模拟鼠标和键盘按键。01基于发送消息的Windows模拟应用程序基于消息机制,对鼠标和键盘的操作也会被系统转换为相应的消息。首先,我们来学习如何通过发送消息来模拟鼠标和键盘操作。1、鼠标和键盘按键的常用消息无论是鼠标指针(或光标)移动、点击,还是键盘按键,在Windows应用程序中通常都会转换成相应的消息。操作鼠标时,用得最多的是移动鼠标和点击鼠标键。例如,教新手使用电脑时,会告诉他将鼠标指针(或光标)移动到“我的电脑”,然后单击鼠标右键,单击鼠标左键选择“属性”弹出式快捷菜单。对话框。当鼠标光标移动时,系统中对应的消息为WM_MOUSEMOVE消息,按下鼠标左键时,对应的消息为WM_LBUTTONDOWN,松开鼠标左键时,对应的消息为WM_LBUTTONUP。在系统中,有很多鼠标消息。在MSDN中找到的鼠标消息如图1所示。图1鼠标相关消息同样,系统也定义了按下和抬起键盘的消息。键盘按下的消息是WM_KEYDOWN,键盘抬起对应的消息是WM_KEYUP。除了这两个消息之外,还有一个消息是比较常用的,就是WM_CHAR消息。键盘消息比鼠标消息少很多,在MSDN中查询到的键盘消息如图2所示。图2键盘相关消息2.PostMessage()函数模拟键盘按键PostMessage()和SendMessage()可以发送消息给指定的窗口。由于鼠标和键盘的按键操作会被系统转换成相应的消息,所以可以使用PostMessage()和SendMessage()来模拟它们的操作,即按下鼠标和键盘按键的消息。对于模拟键盘按键消息,最好使用PostMessage()而不是SendMessage()。在许多情况下,SendMessage()不会成功。现在写一个简单的小工具,使用PostMessage()函数模拟键盘发送信息(发送F5键消息模拟网页刷新)来刷新网页。首先打开VC6.0,创建一个MFC对话框工程,设置界面如图3所示。图3模拟键盘刷新网页界面的布局按照图3所示界面,然后设置控件“开始”按钮的变量。本小程序在“IE浏览器标题”中输入要刷新的页面标题,在“刷新频率”中输入一个刷新时间间隔,单位为秒。当你了解了程序的功能,安排好程序的界面后,就可以开始编写程序的代码了。该程序的代码分为两部分。第一部分是处理“开始”按钮的事件,第二部分是按照指定的时间间隔向指定的浏览器发送按下F5键的消息,刷新网页。首先编写响应“开始”按钮事件的代码,双击“开始”按钮编写其响应事件。代码如下:voidCKeyBoardDlg::OnBtnStart(){//TODO:AddyourcontrolnotificationhandlercodehereCStringstrBtn;intnInterval=0;//获取输入的浏览器标题GetDlgItemText(IDC_EDIT_CAPTION,m_StrCaption);//获取输入的刷新频率nInterval=GetDlgItemInt(IDC_EDIT_INTERVAL,FALSE,TRUE);//判断输入值是否非法if(m_StrCaption==""||nInterval==0){return;}//获取按钮的标题m_Start.GetWindowText(strBtn);if(strBtn=="Start"){//设置定时器SetTimer(1,nInterval*1000,NULL);m_Start.SetWindowText("Stop");GetDlgItem(IDC_EDIT_CAPTION)->EnableWindow(FALSE);GetDlgItem(IDC_EDIT_INTERVAL)->EnableWindow(FALSE);}else{//结束定时器KillTimer(1);m_Start.SetWindowText("开始");GetDlgItem(IDC_EDIT_CAPTION)->EnableWindow(TRUE);GetDlgItem(IDC_EDIT_INTERVAL)->EnableWindow(TRUE);}}代码中,首先判断按钮的文字,如果是“Start”,通过SetTimer()功能;如果按钮的文本不是“开始”,则通过KillTimer()函数关闭计时器。这里的SetTimer()和KillTimer()是MFC中CWnd类的两个成员函数,不是API函数。MFC中很多类成员函数和API函数的写法都是一样的,但还是有区别的。比较一下MFC中SetTimer()的定义和API函数定义的区别。MFC中的定义如下:UINTSetTimer(UINTnIDEvent,UINTnElapse,void(CALLBACKEXPORT*lpfnTimer)(HWND,UINT,UINT,DWORD));API函数定义如下:UINT_PTRSetTimer(HWNDhWnd,UINT_PTRnIDEvent,UINTuElapse,TIMERPROClpTimerFunc);从定义可以看出,MFC中SetTimer()函数的定义比API中SetTimer()函数的定义少了一个参数,即HWND窗口句柄的参数。在MFC中,窗口相关的成员函数不需要指定窗口句柄。MFC内部维护了一个m_hWnd句柄变量(如果想查看或使用MFC内部维护的m_hWnd成员变量,可以直接使用,也可以调用GetSafeHwnd()成员函数获取,推荐第二种方法).在按钮事件中添加定时器,定时器会按照指定的时间间隔进行相应的处理。定时器部分的代码如下:voidCKeyBoardDlg::OnTimer(UINTnIDEvent){//在此处添加处理程序代码HWNDhWnd=::FindWindow(NULL,m_StrCaption.GetBuffer(0));//发送键盘按下消息::PostMessage(hWnd,WM_KEYDOWN,VK_F5,1);Sleep(50);//发送键盘抬起消息::PostMessage(hWnd,WM_KEYUP,VK_F5,1);CDialog::OnTimer(nIDEvent);}定时器的处理很简单,获取窗口句柄即可通过FindWindow()函数进行刷新,然后发送WM_KEYDOWN和WM_KEYUP消息模拟键盘按键。其实在仿真过程中,可以省略WM_KEYUP消息的发送,但是为了让仿真效果更接近真实,建议在仿真过程中成对发送消息。编译连接写好的程序,运行看看效果。在“IE浏览器标题”中输入浏览器的标题,可以通过Spy++获取,然后在“刷新频率”中输入1。然后单击“开始”按钮,观察浏览器每1秒刷新一次。当点击“停止”按钮时,程序将不再模拟浏览器的刷新按钮。至此,通过PostMessage()函数按下F5键模拟键盘按键的程序就完成了。使用PostMessage()函数的优点是目标窗口可以在后台而不需要窗口处于活动状态。您可以将刷新后的浏览器最小化,然后运行小程序刷新网页。可以看到浏览器还在任务栏刷新。02通过API函数模拟鼠标键盘按钮的操作在开发程序的时候,总是依赖发送消息是很辛苦的,因为消息的种类很多,不同的消息类型,不同消息的附件参数也不同。Windows为几乎每一个常用的消息都提供了相应的API函数。为了不用死记硬背太多消息,使用API??函数进行开发还是比较直观的。1、鼠标和键盘按键模拟功能在使用Windows系统消息模拟鼠标或键盘按键操作时可能不够直观和不方便。微软在设计时就考虑到了这一点,所以Windows下的大部分消息都可以直接使用对应的等价API函数,而不用直接发送消息。例如可以使用WM_GETTEXT消息获取文本的内容,对应的函数为GetWindowText()。试想一下,如果程序中所有的函数一眼看去都是SendMessage()和PostMessage(),那岂不是很可怕。下面介绍两个函数,分别用来模拟鼠标和键盘输入。分别是keybd_event()和mouse_event(),定义如下:VOIDkeybd_event(BYTEbVk,BYTEbScan,DWORDdwFlags,ULONG_PTRdwExtraInfo);,ULONG_PTRdwExtraInfo);从函数名可以看出,这两个API函数分别对应键盘事件和鼠标事件。在程序中使用时,对于阅读代码的人来说更直观。接下来使用keybd_event()和mouse_event()这两个函数完成上面刷新网页的小工具。2、对于网页刷新工具keybd_event()和mouse_event()这两个API函数,从函数参数的角度来说,不需要将窗口句柄作为参数传递给它们。那么这两个函数在模拟鼠标键盘的时候一定要激活目标窗口并且在所有窗口的最前面。因此,在程序中首先要做的就是将目标窗口置为最前面,并且处于活动状态。首先看程序的界面部分,如图4所示。图4模拟鼠标和键盘本次的窗口比上一个程序的窗口简单。界面上有两个按钮,第一个按钮“模拟键盘”是通过keybd_event()模拟按下F5键刷新网页,第二个按钮“模拟鼠标”是通过mouse_event()模拟鼠标右键),从而弹出浏览器的快捷菜单,然后使用keybd_event()模拟按下R键刷新网页。知道了程序要实现的功能,首先完成将目标窗口设置为最前面并处于活动状态的部分,代码如下:VOIDCSimInputDlg::FindAndFocus(){GetDlgItemText(IDC_EDIT_CAPTION,m_StrCaption);//判断输入是否为空if(m_StrCaption==""){return;}m_hWnd=::FindWindow(NULL,m_StrCaption.GetBuffer(0));//该函数将创建指定窗口的线程设置到前台//andactivatesthewindow::SetForegroundWindow(m_hWnd);}这个自定义函数很简单,分别调用了FindWindow()和SetForegroundWindow()两个API函数。SetForegroundWindow()函数的使用比较简单。它将指定的窗口设置到最前面并被激活。该函数只有一个参数,即目标窗口的窗口句柄(这里的窗口句柄变量m_hWnd是MFC提供的变量,这个值也可以通过GetSafeHwnd()函数获取。)“模拟键盘”按钮对应的代码如下:voidCSimInputDlg::OnBtnSimkeybd(){//在此处添加处理程序代码//找到窗口//将其设置到前台并激活FindAndFocus();Sleep(1000);//模拟3次F5keybd_event(VK_F5,0,0,0);Sleep(1000);keybd_event(VK_F5,0,0,0);Sleep(1000);keybd_event(VK_F5,0,0,0);}inprogress在模拟键盘按键之前,先调用自定义函数FindAndFocus()将浏览器设置到最前面并激活(在“模拟鼠标”按钮中也必须先调用FindAndFocus()自定义函数)。通过调用keybd_event()函数模拟F5键刷新网页3次。“模拟鼠标”按钮对应的代码如下:voidCSimInputDlg::OnBtnSimmouse(){//在此处添加处理程序代码FindAndFocus();//获取窗口在屏幕上的坐标(x,y)POINTpt={0};::ClientToScreen(m_hWnd,&pt);//设置鼠标位置SetCursorPos(pt.x+36,pt.y+395);//模拟鼠标右键点击//点击鼠标右键后,浏览器会弹出一个快捷菜单mouse_event(MOUSEEVENTF_RIGHTDOWN,0,0,0,0);Sleep(100);mouse_event(MOUSEEVENTF_RIGHTUP,0,0,0,0);Sleep(1000);//0x52=R//弹出右键菜单键后按R//会刷新页面keybd_event(0x52,0,0,0);}代码中用到了两个比较陌生的API函数,ClientToScreen()和SetCursorPos()。它们的定义如下:BOOLClientToScreen(HWNDhWnd,//handletowindowLPPOINTlpPoint//screencoordinates);ClientToScreen()的作用是将窗口区域的坐标转换成屏幕的坐标。更直接的解释是获取指定窗口在屏幕上的坐标位置。BOOLSetCursorPos(intX,//水平位置intY//垂直位置);SetCursorPos()函数的作用是将鼠标光标移动到指定的坐标位置。为什么程序中不使用mouse_event()来移动鼠标光标的位置,而是使用SetCursorPos()的位置呢?在API函数中,与SetCursorPos()对应的函数是GetCursorPos(),SetCursorPos()函数经常与GetCursorPos()函数一起使用。因为在很多情况下,程序在设置好鼠标光标的位置并进行了一系列操作之后,还需要将鼠标光标的位置设置回原来的位置,所以在调用SetCursorPos()之前,需要调用GetCursorPos()获取鼠标光标的当前位置,以便在操作完成后可以将鼠标光标设置到其原始位置。从这里也可以看出,很多API函数都是成对出现的,包括Set和Get,在记忆的时候非常方便。程序中调用SetCursorPos()函数时,参数中的x坐标和y坐标加上了两个整型常量,可能会造成混淆。这两个整型常量的作用是通过ClientToScreen()函数获取浏览器左上角的x和y坐标,浏览器的鼠标右键菜单必须在客户区激活浏览器,所以需要在左上角坐标的基础上加上两个偏移量。代码中的两个整型常量是一个偏移量(这里的偏移量值可以随意修改,只要鼠标能放在浏览器窗口中即可)。很多地方都会用到鼠标键盘按键的模拟。比如有些病毒利用模拟鼠标点击杀毒软件的警告提示,比如游戏辅助工具通过模拟鼠标快速点击……鼠标键盘按键的模拟并不简单。一般情况下,可以通过上面介绍的内容进行鼠标和键盘按键的模拟操作。但在某些情况下不起作用,例如有些游戏会过滤PostMessage()函数发送的消息,有些游戏会hookkeybd_event()和mouse_event()函数,有些游戏使用DX来响应鼠标和键盘...
