最近需要在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秒鍾沒有輸入就調暗背光,用軟件定時器就很容易實現。最終的測試結果很滿意,特將心得記錄於此。