WinCE键盘钩子与鼠标钩子

时间:2022-11-02 00:33:22

=============================================================
标题:WinCE键盘钩子与鼠标钩子
摘要:
备注:Windows CE + VC2005
日期:2010.8.20
姓名:朱铭雷
=============================================================
    最近需要在Windows CE 5.0操作系统下实现这样的功能:如果没有用触摸笔或者鼠标点击屏幕,则等待5秒钟之后自动调暗背光。一旦屏幕有输入,也就是接受到了触摸笔或者鼠标的点击,则自动调亮背光。一般的手机都有这样的功能。至于调节背光的功能,已经在底层做好了,我可以通过接口函数来调节。剩下的问题就是感应这个屏幕点击,那么一下就想到了这个鼠标钩子。道理也很简单,当有鼠标消息发生时,操作系统会首先交给我自己的钩子过程,等我享用完了,再交给钩子链中的下一个钩子或者目标窗口。
    想法有了,马上开始实施,第一步就受挫了。诸如:
SetWindowsHookEx,CallNextHookEx,UnhookWindowsHookEx这些函数在Windows CE下都没有提供。看了下微软的MSDN,Windows CE不支持钩子,特别失望。不过还是去google了一下,也问了下群里的朋友。幸亏没有放弃,微软说不支持,但是还是留了“余地”的,也就是想想办法,最常用的键盘钩子与鼠标钩子在Windows CE系统下还是可以使用的。但也只是留了一点“余地”,因为Windows CE下使用钩子只能“捕获”,不能“拦截”。但只要能“捕获”,我就很满足了,因为我的目的也不是要去拦截,我没那么“贪婪”,哈哈。
    先看WinCE系统下与“钩子”有关的几个函数:
HHOOK SetWindowsHookEx(int idHook,
    HOOKPROC lpfn,
    HINSTANCE hMod,
    DWORD dwThreadId
); // 用来安装钩子

LRESULT CallNextHookEx(HHOOK hhk,
    int nCode,
    WPARAM wParam,
    LPARAM lParam
); // 将消息传递给下一个钩子

BOOL UnhookWindowsHookEx(HHOOK hhk
); // 移除钩子

    上面这几个函数在桌面Windows系统下可以直接使用,但是要在Windows CE系统下使用,还得有点曲折。因为Windows CE系统将这几个函数给“雪藏”起来了,现在就把它找出来。找到coredll.dll动态链接库文件,搞过Windows CE的朋友相信都知道这个coredll.dll。然后用工具“解剖”一下,dumpbin也行,eXeScope也可。
    WinCE键盘钩子与鼠标钩子
    WinCE键盘钩子与鼠标钩子
    WinCE键盘钩子与鼠标钩子
    从上面这几个图就可以看出,这几个函数在Windows CE系统下也是可以搞到的。只不过需要我们自己去LoadLibrary一下coredll.dl,然后在GetProcAddress分别获取如上三个函数的地址,之后就可以使用了。
    但是利用SetWindowsHookEx函数只能够去设置键盘钩子,却搞不定鼠标钩子。要想搞定鼠标钩子,还得用另外两个函数:QASetWindowsJournalHook和QAUnhookWindowsJournalHook。这两个函数在pwinuser.h头文件中定义:(所以使用之前当然要#include "pwinuser.h")
HHOOK
WINAPI
QASetWindowsJournalHook(
    int  nFilterType,
    HOOKPROC pfnFilterProc,
    EVENTMSG *pfnEventMsg
    );

BOOL
WINAPI
QAUnhookWindowsJournalHook(
    int  nFilterType
    );

    说到这,先给出几行关键的代码:
———————————————————————————————————————
     // 加载coredll.dl,获取相关函数地址
    typedef HHOOK   (WINAPI *_SetWindowsHookExW)(int, HOOKPROC, 
HINSTANCE, DWORD);
    typedef LRESULT (WINAPI *_CallNextHookEx)(HHOOK, int, WPARAM, LPARAM);
    typedef LRESULT (WINAPI *_UnhookWindowsHookEx)(HHOOK);

 

    static _SetWindowsHookExW SetWindowsHookExW;
    static _CallNextHookEx         CallNextHookEx;
    static _UnhookWindowsHookEx UnhookWindowsHookEx;

    hInstDll = LoadLibrary(TEXT("coredll.dll"));
    if(hInstDll == NULL)
         return FALSE;

    SetWindowsHookExW  =  (_SetWindowsHookExW)GetProcAddress(hInstDll, 
TEXT("SetWindowsHookExW"));
    if(SetWindowsHookExW  == NULL)
         return FALSE;

    CallNextHookEx  =  (_CallNextHookEx)GetProcAddress(hInstDll,
TEXT("CallNextHookEx"));
    if(CallNextHookEx == NULL)
        return FALSE;

    UnhookWindowsHookEx  =  (_UnhookWindowsHookEx)GetProcAddress(hInstDll, TEXT("UnhookWindowsHookEx"));
    if(UnhookWindowsHookEx == NULL)
        return FALSE;

 

    // 安装键盘钩子
    hHookKbd = SetWindowsHookExW(WH_KEYBOARD_LL, KbdHookCallback, g_hInst, 0);

    if(hHookKbd == NULL)
        return FALSE;

    // 安装鼠标钩子
    EVENTMSG msg = {HC_ACTION};
    hHookMouse = QASetWindowsJournalHook(WH_JOURNALRECORD, 

MouseHookCallback, &msg);
    if(hHookMouse == NULL)
       return FALSE;

    // 移除钩子
    QAUnhookWindowsJournalHook(WH_JOURNALRECORD);
       hHookMouse = NULL;

    UnhookWindowsHookEx (hHookKbd);
       hHookKbd = NULL;
———————————————————————————————————————
    到了这,再把一些杂七杂八的知识点介绍一下:
    1 全局钩子
    因为我的需求是要捕获当前正在运行的所有进程的鼠标消息,所以我必须要用全局钩子,也就是将安装钩子的代码放到动态链接库中去实现。(关于什么是“进程内钩子”和“全局钩子”,推荐大伙去看看孙鑫先生的《VC++深入详解》第20章,我觉得讲的很清楚。)值得一提的是:据说(我自己还没来得及测试)在Windows CE系统下,只能够使用全局钩子,不能够使用进程内钩子。如果有人测试了,欢迎留言相告。(我个人的感觉是可以使用)
    2 Windows CE系统下支持的三种钩子
    #define WH_JOURNALRECORD    0
    #define WH_JOURNALPLAYBACK  1
    #define WH_KEYBOARD_LL      20

    对于这三种钩子做下简单介绍,该介绍来源于网友的一篇博文,我引用至此并感谢作者。原文地址:http://hi.baidu.com/gensoft/blog/item/a946c539e2ce132897ddd889.html

    “wince下只支持三种钩子:
    1.#define WH_JOURNALRECORD 0
使应用程序可以监视输入事件。典型地,应用程序使用该HOOK记录鼠标、键盘输入事件以供以后回放。该HOOK是全局HOOK,并且不能在指定线程中使用。
    2.#define WH_JOURNALPLAYBACK 1
使应用程序可以向系统消息队列中插入消息。该HOOK可以回放以前由WH_JOURNALRECORD HOOK录制的鼠标、键盘输入事件。在WH_JOURNALPLAYBACK Hook安装到系统时,鼠标、键盘输入事件将被屏蔽。该HOOK同样是一个全局HOOK,不能在指定线程中使用。WH_JOURNALPLAYBACK Hook返回一个时间暂停值,它告诉系统,在处理当前回放的消息时,系统等待百分之几秒。这使得此HOOK可以控制在回放时的时间事件
    3.#define WH_KEYBOARD_LL 20
其中最常用的是键盘钩子。据本人实践,这个钩子也不能在指定线程中使用。”

    3 关于键盘钩子回调函数
    LRESULT CALLBACK KbdHookCallback(int nCode, WPARAM wParam, LPARAM lParam) 
    {
         ... ...
    }
    其参数含义与桌面版本有些差异,先来说桌面版本。桌面版本中参数wParam保存的是产生当前按键消息的键盘按键的虚拟键代码,lParam是一个32位的整数,其每一位或者某些位分别代表特定的含义。如其第29位用来表示Alt按键是否被按下。再来说Windows CE版本,参数wParam用来指示产生当前按键消息的按键是按下还是弹起,lParam保存的是一个KBDLLHOOKSTRUCT结构体指针。该结构体如下:
    typedef struct tagKBDLLHOOKSTRUCT {
        DWORD vkCode;      // virtual key code
        DWORD scanCode;    // scan code    
        DWORD flags;        // flags unused
        DWORD time;        // time stamp for this message
        DWORD dwExtraInfo;  // extra info from the driver or keybd_event
    } KBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;

    虚拟键代码存储在该结构体的vkCode成员中。
    4 最后介绍两点在调试过程中遇到的问题,我在网上也见到有人有类似的记录
首先是我在鼠标钩子中,使用::MessageBox弹出一个提示,但没有成功。但是我在鼠标钩子中向调用线程SendMessage一个自定义的消息是成功的,也就是鼠标钩子过程函数是被调用了的,但是这个MessageBox为什么不弹出提示,我还不得而知。其次是我连接调试的时候,鼠标钩子过程函数没有被调用执行,而生成的可执行文件执行起来,鼠标钩子确实管用的。
    附录:
    我在写程序过程中,参考的几篇有价值的帖子和博文,地址如下:
    http://simplaman.itpub.net/post/2120/474664?SelectActiveColorSchema=1
    http://jkflyfox.spaces.live.com/blog/cns%21C936FCDDF997BA5F%211449.entry
    http://hi.baidu.com/gensoft/blog/item/a946c539e2ce132897ddd889.html
    http://blog.csdn.net/91program/archive/2007/12/23/1961570.aspx
    http://topic.csdn.net/u/20100430/17/81cd558c-05d7-4e2b-a1b3-e7029e3f6c97.html
    最后在说说我那个调亮调暗背光的事,我在鼠标钩子过程函数中向我的调用线程发送了一个自定义的消息:::SendMessage(g_hWnd, WM_DEFMOUSEDOWN, 0, 0);,然后在我的调用程序中响应这个自定义的消息WM_DEFMOUSEDOWN,在该消息响应函数中调亮背光。至于等待5秒钟没有输入就调暗背光,用软件定时器就很容易实现。最终的测试结果让我很满意。特将心得记录于此,与以后需要的朋友分享。

    以上内容的错误之处,希望看过的朋友帮我改正一下。