- C++ 黑客编程揭秘与防范(第3版)
- 冀云
- 1817字
- 2020-06-27 17:48:50
1.3.1 基于发送消息的模拟
通过前面的介绍,我们已经明白,Windows的应用程序是基于消息机制的,对于鼠标和键盘的操作也会被系统转化为相应的消息。首先来学习如何通过发送消息进行鼠标和键盘的模拟操作。
1.鼠标、键盘按键常用的消息
无论是鼠标指针(或光标)的移动、单击,还是键盘的按键,通常在Windows应用程序中都会转换成相应的消息。在操作鼠标时,使用最多的是移动鼠标和单击鼠标键。比如,在教新手使用计算机时会告诉他,将鼠标指针(或光标)移动到“我的电脑”上,然后单击鼠标右键,在弹出的快捷菜单中用鼠标左键单击选择“属性”对话框。当移动鼠标光标的时候,系统中对应的消息是WM_MOUSEMOVE消息,按下鼠标左键时的对应的消息是WM_LBUTTONDOWN,释放鼠标左键时,对应的消息是WM_LBUTTONUP。在系统中,鼠标的消息有很多。在MSDN中查询到的鼠标消息如图1-8所示。
图1-8 鼠标相关消息
同样,在系统中也定义了键盘的按下与抬起的消息。键盘按下的消息是WM_KEY DOWN,与之对应的键盘抬起的消息是WM_KEYUP。除了这两个消息外,还有一个消息是比较常用的,这个消息在前面介绍消息循环时提到过,就是WM_CHAR消息。键盘的消息相对于鼠标要少很多,在MSDN中查询到的键盘消息如图1-9所示。
图1-9 键盘相关消息
2.PostMessage()函数对键盘按键的模拟
通过前面的介绍,我们已经知道,PostMessage()和SendMessage()这两个函数可以对指定的窗口发送消息。既然鼠标和键盘按键的操作被系统转换为相应的消息,那么就可以使用PostMessage()和SendMessage()通过按鼠标和键盘按键发送的消息来模拟它们的操作。对于模拟键盘按键消息,最好使用PostMessage()而不要使用SendMessage()。在很多情况下,SendMessage()是不会成功的。
现在编写一个简单的小工具,它通过PostMessage()函数模拟键盘发送(发送F5键的消息来模拟网页的刷新)的信息来刷新网页。首先打开VC6.0,创建一个MFC对话框工程,按照图1-10所示设置界面。
图1-10 模拟键盘刷新网页界面布局
按照图1-10所示的界面进行布局,然后为“开始”按钮设置控件变量。这个小程序在“IE浏览器标题”处输入要刷新的页面的标题,在“刷新频率”处输入一个刷新的时间间隔,单位是秒。
当了解程序的功能并且将程序的界面布置好以后,就可以开始编写程序的代码了。程序的代码分为两部分,第一部分是程序要处理“开始”按钮的事件,第二部分是要按照指定的时间间隔对指定的浏览器发送按F5键的消息来刷新网页。
首先来编写响应“开始”按钮事件的代码,双击“开始”按钮来编写它的响应事件。代码如下:
void CKeyBoardDlg::OnBtnStart() { // TODO: Add your control notification handler code here CString strBtn; int nInterval = 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 == "开始" ) { // 设置定时器 SetTimer(1, nInterval * 1000, NULL); m_Start.SetWindowText("停止"); 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); } }
在代码中,首先判断按钮的文本,如果是“开始”,则通过SetTimer()函数设置一个定时器;如果按钮的文本不是“开始”,则通过KillTimer()函数关闭定时器。
这里的SetTimer()和KillTimer()是MFC中CWnd类的两个成员函数,不是API函数。很多MFC中的类成员函数和API函数的写法是一样的,但是它们还是有区别的。比较一下SetTimer()在MFC中的定义和API函数的定义的差别。
MFC中的定义如下:
UINT SetTimer( UINT nIDEvent, UINT nElapse, void (CALLBACK EXPORT* lpfnTimer)( HWND, UINT, UINT, DWORD) );
API函数的定义如下:
UINT_PTR SetTimer( HWND hWnd, UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc );
从定义中可以看出,MFC中SetTimer()函数的定义比API中SetTimer()函数的定义少了一个参数,即HWND的窗口句柄的参数。在MFC中,窗口相关的成员函数都不需要指定窗口句柄,在MFC的内部已经维护了一个m_hWnd的句柄变量(如果想要查看或使用MFC内部维护的m_hWnd成员变量,可以直接使用它,也可以通过调用GetSafeHwnd()成员函数来得到它,推荐使用第二种方法)。
在按钮事件中添加定时器,那么定时器会按照指定的时间间隔进行相应的处理。定时器部分的代码如下:
void CKeyBoardDlg::OnTimer(UINT nIDEvent) { // 在此处添加处理程序代码 HWND hWnd = ::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()函数的好处是目标窗口可以在后台,而不需要窗口处于激活状态。可以将被刷新的浏览器最小化,然后运行刷新网页的小程序,在任务栏可以看到浏览器仍然在不断刷新。