WinCE鍵盤鈎子與鼠標鈎子


最近需要在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系統將這幾個函數給“雪藏”起來了。找到coredll.dll動態鏈接庫文件,搞過Windows CE的朋友都知道這個coredll.dll。然后用eXeScope看一下。

從上圖可以看出,這幾個函數在Windows CE系統下也是可以搞到的。只不過需要LoadLibrary一下coredll.dll,然后再GetProcAddress分別獲取三個函數的地址,之后就可以使用了。

但是利用SetWindowsHookEx函數只能夠去設置鍵盤鈎子,卻搞不定鼠標鈎子。鼠標鈎子還得用到另外兩個函數:QASetWindowsJournalHook和QAUnhookWindowsJournalHook。這兩個函數在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秒鍾沒有輸入就調暗背光,用軟件定時器就很容易實現。最終的測試結果很滿意,特將心得記錄於此。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM