4.3  鼠标与键盘的模拟

4.3 鼠标与键盘的模拟

  • 最近更新2018年09月27日

特殊说明:版权归个人所有,请勿转载,谢谢合作。

4.3.1    发送消息函数

鼠标与键盘消息已经学过,但之前学到的消息是如何捕获,接下来讲解的内容是如何去产生一个消息。自行传递消息的方法有两种:SendMessage与PostMessage。

SendMessage函数功能是,将指定的消息发送到一个或多个窗口。此函数为指定的窗口调用过程处理程序,直到过程处理函数处理完消息再返回。其函数原型如下:

LRESULT SendMessage(
  HWND hWnd,        // handle of destination window
  UINT Msg,         // message to send
  WPARAM wParam,    // first message parameter
  LPARAM lParam     // second message parameter
);

 

参数hWnd,其窗口程序将接收消息的窗口的句柄。如果此参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,其中包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口;

参数Msg,指定被发送的消息,鼠标及键盘消息均可;

参数wParam,根据消息的不同,指定附加不同的消息信息,如果为鼠标消息存放标志,如果为键盘消息,则存放虚拟键值;

参数lParam,根据消息的不同,指定附加不同的消息信息,如果为鼠标消息存放当前光标的X、Y值,如果为键盘消息,则存放键盘扫描码等信息;

函数返回值,指定消息处理的结果,依赖于所发送的消息。SendMessage函数实际上调用了过程处理函数,所以返回值就是过程处理函数的返回结果。

PostMessage函数功能是,将一个消息放入(寄送)到与指定窗口创建的线程相联系消息队列里,不等待线程处理消息就返回。消息队列里的消息通过调用GetMessage和PeekMessage取得。其函数原型如下:

 

BOOL PostMessage(
  HWND hWnd,        // handle of destination window
  UINT Msg,         // message to post
  WPARAM wParam,    // first message parameter
  LPARAM lParam     // second message parameter
);

 

PostMessage函数的参数与SendMessage函数的参数含义相同,唯一不同的是函数返回值,PostMessage函数如果调用成功,返回非零值;如果函数调用失败,返回值为零。PostMessage函数只是将一个消息,不经过任何处理,放入到窗口的消息队列中。

SendMessage和PostMessage函数非常有意思,通过这两个函数,可以对任务程序做任何控制。【例4-3】实现两个很有意思的功能:

(1)点击鼠标右键时,记事本窗口自动关闭;

(2)在“MyWin”窗口输入内容时,会在记事本中显示。

虽然在界面中体现不出具体的操作过程,但可以通过代码进行详细地分析。这个程序一定要自己动手做一遍,它会激发无限的想像空间,代码如下所示:

 

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  HWND hChildWnd = NULL;				// 存放子窗口句柄
  HWND hWinWnd = NULL;				// 存放父窗口句柄

  // 子窗口句柄直接赋值
  hChildWnd = (HWND)0x003B05A4;

  // 获得父窗口句柄
  hWinWnd = GetParent(hChildWnd);

  // 消息处理
  //
  switch (message) 
  {
    // 鼠标消息事件
  case WM_RBUTTONDOWN:
    // 发送销毁窗口的消息
    //PostMessage(hWinWnd, WM_DESTROY, NULL, NULL);
    SendMessage(hWinWnd, WM_DESTROY, NULL, NULL);
    break;

    // 键盘消息事件
  case WM_KEYDOWN:
    // 键盘按下时,发送按下的内容。
    PostMessage(hChildWnd, message, wParam, lParam);
    break;
  case WM_KEYUP:
    // 键盘抬起时,发送抬起的内容。
    PostMessage(hChildWnd, message, wParam, lParam);
    break;

    // 窗口销毁消息,关闭窗口时响应。
  case WM_DESTROY:
    PostQuitMessage(0);
    break;

  default:
    // 调用系统默认消息处理,即交给系统处理。
    return DefWindowProc(hWnd, message, wParam, lParam);
  }//end switch

  return 0;
}

 

程序执行效果,如图4.4所示。

  • 图4.4 发送消息

 

在图中不难看出,实现这个功能,需要有一个记事本做为控制对象。程序中定义了两个句柄类型的变量hChildWnd与hWinWnd,分别用于存放记事本子窗口与父窗口的句柄。应该记得前面提到的句柄的概念,句柄是一个4个字节的整数,用于存放唯一标识窗口的ID。

程序中子窗口句柄,直接进行的赋值操作,那“0x003B05A4”从何而来呢?如果VC6.0安装较为完整,那么,就可以使用VC6.0提供的Spy++工具来获得应用程序(记事本)的句柄,具体操作过程如下所示:

首先,在点击VC6.0的【Tools】目录中的【Spy++】选项,如图4.5所示;

  • 图4.5  启动Spy++工具

 

其次,在弹出的Spy++工具界面中,在工具栏点击【】按钮,则弹出“Window Search”窗口,如图4.6所示;

  • 图4.6 Window Shearch窗口

 

再次,鼠标移动到“Finder Tool”后面的处,按下鼠标左键(要记住是按住不放),移动光标到记事本的客户区,当记事本客户区周围出现黑色边框,说明捕获成功,此时放开鼠标左键,如图4.7所示;

  • 图4.7 捕获记事本句柄

 

最后,在“Window Search”窗口中的“Handle”处,拷贝句柄(当前句柄为003B05A4)到程序中。此处的句柄为十六进程,所以在程序赋值时,需要在前面加入“0x”。十六进制值直接赋值给HWND类型,是不允许的,所以需要进行强制类型转换(强制类型转换代码:(HWND)0x003B05A4)。

需要注意的是,句柄是唯一标识窗口的,所以你所获得的句柄值不可能与实例相同。即使是同样是记事本,关闭后再次打开,句柄也将发生变化。

程序中使用GetParent函数,实现获得窗口父句柄。这个函数有一个参数,即为当前子窗口的句柄,如果当前窗口已经为顶层窗口,则返回值为NULL。

捕获鼠标右键按下事件(WM_RBUTTONDOWN),使用PostMessage或SendMessage发送窗口销毁消息(WM_DESTROY)。这里使用的句柄是窗口的句柄。

捕获键盘的按下消息(WM_KEYDOWN)、抬起消息(WM_KEYUP)。虽然只捕获按下消息此功能也能实现,但这不符合规则,键盘的一次按键会产生按下与抬起两个消息。在发送键盘内容时,只能使用PostMessage函数才能实现(SendMessage函数不好用)。这里使用句柄是记事本的子窗口,即输入的客户区部分的句柄。

不得不再介绍一下SendMessage与PostMessage函数。它们都是发送消息的函数,当鼠标右键按下之后,PostMessage函数将窗口销毁消息发送到记事本消息队列中,当记事本使用GetMessage函数获得到WM_DESTROY时,记事本将执行销毁操作;SendMessage函数将窗口销毁消息发送到记事本的回调函数中,记事本直接执行销毁操作。

在键盘按下与抬起消息中,之所以不能使用SendMessage函数,是因为要发送的内容是记事本的客户区域窗口,而不是整个窗口。同样可以判断出,记事本的客户区域窗口,本身没有过程处理函数单独为它服务,而它的控制是使用记事本本身的过程处理函数进行的处理。

 

4.3.2    鼠标消息模拟

除了SendMessage与PostMessage函数,还可以应用mouse_event函数来实现鼠标按键的模拟,函数原型如下所示。

VOID mouse_event(
  DWORD dwFlags,  // flags specifying various motion/click variants
  DWORD dx,       // horizontal mouse position or position change
  DWORD dy,       // vertical mouse position or position change
  DWORD dwData,   // amount of wheel movement
  DWORD dwExtraInfo 
                  // 32 bits of application-defined information
);

 

参数dwFlags,选项标志。dwFlags的值为MOUSEEVENTF_LEFTDOWN时表示左键按下,为MOUSEEVENTF_LEFTUP表示左键抬开,向系统发送相应消息。此参数可以是表4.6中“值”列的任何合理组合(各值以“|”组合)。

 

  • 表4.1 dwFlags标记值
描述
MOUSEEVENTF_ABSOLUTE 根据dx与dy的值来确定屏幕的相对坐标位置
MOUSEEVENTF_MOVE 鼠标移动
MOUSEEVENTF_LEFTDOWN 模拟鼠标左键按下
MOUSEEVENTF_LEFTUP 模拟鼠标左键抬起
MOUSEEVENTF_RIGHTDOWN 模拟鼠标右键按下
MOUSEEVENTF_RIGHTUP 模拟鼠标右键抬起
MOUSEEVENTF_MIDDLEDOWN 模拟鼠标中键按下
MOUSEEVENTF_MIDDLEUP 模拟鼠标中键抬起
MOUSEEVENTF_WHEEL 鼠标滚轮滚动

 

参数dx、dy,指定鼠标分别沿x、y轴的绝对位置。如果指定了MOUSEEVENTF_ABSOLUTE值,则dX和dy含有标准化的绝对坐标,其值在0到65535之间。事件程序将此坐标映射到显示界面。坐标(0,0)映射到显示界面的左上角,(65535,65535)映射到右下角。如果没指定MOUSEEVENTF_ABSOLUTE,dx和dy表示相对于上次鼠标事件产生的位置(即上次报告的位置)的移动。正值表示鼠标向右(或下)移动;负值表示鼠标向左(或上)移动。

参数dwData,如果dwFlags为MOUSEEVENTF_WHEEL,则dwData指定鼠标滚轮移动的数量。正值表明鼠标滚轮向前转动,负值表明鼠标滚轮向后转动,滚动一次定义为一个WHEEL_DELTA,即120。如果dwFlagss不是MOUSEEVENTF_WHEEL,则dwData值为零。

参数dwExtraInfo,用来指定与鼠标事件相关的附加32位值(扩展信息)。应用程序调用GetMessageExtraInfo函数来获得此附加信息。

最后两个参数并不重要,一般设置为0即可。根据mouse_event函数的学习,下面给出使用mouse_event函数模拟的鼠标点击事件(注意:一次鼠标点击事件,包括按下与抬起)。

 

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

 

mouse_event函数经常配合SetCursorPos、GetCursorPos函数一起使用。SetCursorPos函数原型如下:

BOOL SetCursorPos(
  int X,  // horizontal position
  int Y   // vertical position
);

 

SetCursorPos函数的作用是把光标移到屏幕的指定位置。参数X指定光标的新的X坐标,以屏幕坐标表示;参数Y指定光标的新的Y坐标,以屏幕坐标表示。

GetCursorPos函数的作用是获取光标的位置,以屏幕坐标表示。其函数原型如下:

BOOL GetCursorPos(
  LPPOINT lpPoint   // address of structure for cursor position
);

 

参数lpPoint,指向POINT结构的指针,该结构体接收光标的屏幕坐标。POINT结构体内部有两个变量,一个整型的x,另一个是整型的y,分别用来代表屏幕取的坐标位置。

 

4.3.3    键盘消息模拟

除了SendMessage与PostMessage函数,还可以应用keybd_event函数来实现键盘的模拟,函数原型如下所示。

 

VOID keybd_event(
  BYTE bVk,           // virtual-key code
  BYTE bScan,         // hardware scan code
  DWORD dwFlags,      // flags specifying various function options
  DWORD dwExtraInfo   // additional data associated with keystroke
);

 

参数bVk,定义一个虚拟键值码。键码值必须在1~254之间。

参数bScan,定义该键的硬件扫描码。

参数dwFlags,定义函数操作的各个方面的一个标志位集。应用程序可使用如表4.7所示的一些预定义常数的组合设置标志位。

 

  • 表4.2 dwFlags标记值
描述
KEYEVENTF_EXTENDEDKEY 若指定该值,则扫描码前一个值为0xE0(224)的前缀字节
KEYEVENTF_KEYUP 若指定该值,该键将被释放。若未指定该值,该键将被按下

参数dwExtraInfo,定义与键盘事件相关的附加的32位值。

keybd_event函数能触发一个按键事件,也就是说会产生一个WM_KEYDOWN或WM_KEYUP消息。当然也可以用这两个消息来模拟按键,但是没有直接用这个函数方便。例如,模拟输入“a”(a的ASCII码值为65),代码实现如下:

 

keybd_event(65,0,0,0);
keybd_event(65,0,KEYEVENTF_KEYUP,0);

 

代码中只实现了KEYEVENTF_KEYUP,这样合理吗?这样的疑问交给你自己去思考。

 

分享到 :
相关推荐

发表回复

登录... 后才能评论