WinCE系统鼠标键盘钩子使用方法

 2017-5-26     作者:杨阳    

  在实际应用中,有时程序需要在特定的鼠标、按键事件后,执行一些操作,比如:

  1、点击触摸屏时触发蜂鸣器。

  2、在休眠状态点击触摸屏后,让程序结束休眠状态。

  3、设定全局的按键功能,如一键截图或一键关机。

  4、过滤一些鼠标或键盘操作等等。


  这时,可以使用键盘鼠标钩子来实现这些功能。


  钩子是WINDOWS/WINCE系统独有的消息处理机制。通过系统调用,将消息处理程序段挂入系统,获得消息处理优先控制权,在消息达到目的窗口前进行处理。钩子函数可以通过判断决定是否加工处理(改变)消息,或不做处理继续传递各消息,或强制结束消息传递。

  钩子有很多种,WINCE系统已精简到只有4种钩子。同时钩子又可做局部钩子或全局钩子,局部钩子仅在指定进程内生效,而全局钩子在系统范围内都生效。一般的全局钩子需要挂载到dll中使用,本文介绍的鼠标钩子和键盘钩子较特殊,不需要挂载到dll中即可全局生效。

  钩子的运行逻辑如下图,每种钩子可以挂入任意多个钩子函数,以链表方式存储。系统优先访问链表首位的钩子,然后依次传递消息给后面的钩子处理。后挂入的钩子位于链表的前端。


WINCE系统鼠标键盘钩子使用方法.gif


使用方法

  添加钩子分3步。

  1、编写钩子函数处理代码

  2、调用API函数将钩子函数挂入系统

  3、创建消息循环,使钩子运作起来


  添加引用

  使用钩子需要用到函数,SetWindowsHookEx,UnhookWindowsHookEx,CallNextHookEx。及键盘钩子鼠标钩子的定义,及键盘消息,鼠标消息的结构体定义,均定义在pwinuser.h中。

  #include "pwinuser.h"


  创建需要挂入系统的消息处理函数

  即钩子函数,钩子函数定义必须为制定的格式。

  钩子函数根据实际应用需求,决定是否调用CallNextHookEx,将消息传递给后面的钩子处理。

  首位的钩子函数返回值决定该消息是丢弃,还是传给系统消息处理函数,再分发给各窗口。

  以下为键盘及鼠标钩子函数的示例。


  键盘钩子函数

  该键盘钩子示例函数中,当检测到按键‘1’按下时,调用Beep函数触发蜂鸣器。

  LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)

  {

    KBDLLHOOKSTRUCT *pkbhs = (KBDLLHOOKSTRUCT *) lParam;

    if(wParam == WM_KEYDOWN)         //按钮按下

    {

      switch(pkbhs->vkCode)

      {

        case 0x31:    //按钮‘’

          Beep();

          break;

        defaut:

          break;

      }

    }

    return CallNextHookEx(NULL,nCode,wParam,lParam);

  } 


  注:

  1、WINCE键盘钩子函数得到的WPARAM记录的消息类别,如按键是按下消息WM_KEYDOWN还是弹起消息WM_KEYUP。

    LPARAM指向键盘消息结构体KBDLLHOOKSTRUCT,里面记录有详细信息,如触发的按键是什么,有没有辅助按下ALT键或者CTRL键,等等。

  2、钩子队列第一个钩子的返回值决定系统是否能接受到该消息。

    返回0表示,将消息传递给系统消息处理函数继续处理。

    返回1表示,丢弃消息,系统将收不到该消息。

    调用CallNextHookEx表示调用下一个钩子消息处理函数,如果不调用,那么后面的钩子函数将不产生作用。


  鼠标(触摸屏)钩子函数

  该鼠标钩子示例函数中,当检测到鼠标或触摸屏点击时,调用Beep函数触发蜂鸣器。

  LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)

  {

    MSLLHOOKSTRUCT *pmshs = (MSLLHOOKSTRUCT *) lParam;

    if(wParam == WM_LBUTTONDOWN)     //鼠标点击处理代码

    {

      Beep();

    }

    return CallNextHookEx(NULL,nCode,wParam,lParam);

  }


  注:

  1、WINCE鼠标钩子函数得到的WPARAM记录的消息类别,如是鼠标按下还是弹起,还是双击,移动,等等。

    LPARAM记录的详细信息,如点击坐标,滚轮参数等等。

  2、触摸屏消息和鼠标消息是一样的。

  3、钩子队列第一个钩子的返回值决定系统是否能接受到该消息。

     返回0表示,将消息传递给系统消息处理函数继续处理。

     返回1表示,丢弃消息,系统将收不到该消息。

     调用CallNextHookEx表示调用下一个钩子消息处理函数,如果不调用,那么后面的钩子函数将不产生作用。


  Beep函数

  在本文示例中,将嵌入式板子GPIO15连接上一个蜂鸣器,通过设置GPIO电平触发蜂鸣器。


  在程序初始段打开GPIO,获得GPIO句柄。

  #include "isa_dio.h"

  HANDLE hGpio;

  hGpio = OpenGPIO( _T("PIO1:"));

 

  添加Beep函数,通过GPIO句柄操作GPIO15。

  void Beep()

  {

              GPIO_OutClear(hGpio, GPIO15);

              Sleep(10);

              GPIO_OutSet(hGpio, GPIO15);

  }

 

  添加钩子

  在主线程中调用SetWindowsHookEx函数向系统添加钩子。

  第一个参数为添加的钩子类型,WH_KEYBOARD_LL为键盘钩子,WH_MOUSE_LL为鼠标钩子。

  第二个参数为注入的消息处理函数指针,即前面定义的钩子函数。

  第三个参数为钩子程序的instance指针,本文介绍的钩子并不需要挂载到dll中使用,所以简单设置为NULL即可。

  最后个参数为钩子关联的线程句柄,这里为0表示关联所有线程,即全局钩子。

  函数返回NULL表示添加钩子失败,成功返回钩子句柄。

  示例代码如下:

  HINSTANCE          hInstance = NULL;

  HHOOK         g_hKBDhook = NULL;

  HHOOK              g_hMouseHook = NULL;

 

  g_hKBDhook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, hInstance, 0);

  if(g_hKBDhook == NULL)

  {

        return false;

  }

  g_hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, hInstance, 0);

  if(g_hMouseHook == NULL)

  {

        return false;

  }


  添加消息循环

  钩子机制是基于消息循环的,所以添加消息循环是必须的,如果没有添加消息循环,会导致系统消息进入钩子后卡死。

  MFC框架的程序已经自带消息循环。

  而命令行的程序则需要在主函数里添加以下代码。

  MSG msg;

  while(GetMessage(&msg, NULL, 0, 0)) {

    TranslateMessage(&msg);

    DispatchMessage(&msg);

  }

 

  删除钩子

  当不需要钩子时,应当主动释放钩子。

  UnhookWindowsHookEx(g_ hKBDhook);

  UnhookWindowsHookEx(g_hMouseHook);

 

  英创提供了例程及源代码,有需要的客户可以联系英创工程师获得。