一,、引言
鉤子的本質(zhì)是一段用以處理系統(tǒng)消息的程序,,通過(guò)系統(tǒng)調(diào)用,把它掛入系統(tǒng),。鉤子的種類很多,,每種鉤子可以截獲并處理相應(yīng)的消息,每當(dāng)特定的消息發(fā)出,,在到達(dá)目的窗口之前,,鉤子程序先行截獲該消息、得到對(duì)此消息的控制權(quán),。此時(shí)鉤子函數(shù)可以對(duì)截獲的消息進(jìn)行加工處理,,甚至可以強(qiáng)制結(jié)束消息的傳遞。這有點(diǎn)類似與MFC中的PreTranslateMessage函數(shù),,所不同的是該函數(shù)只能用于攔截本進(jìn)程中的消息,,而對(duì)系統(tǒng)消息則無(wú)能為力。 二,、Win32系統(tǒng)鉤子的實(shí)現(xiàn) 每種類型的鉤子均由系統(tǒng)來(lái)維護(hù)一個(gè)鉤子鏈,,最近安裝的鉤子位于鏈的開(kāi)始,擁有最高的優(yōu)先級(jí),,而最先安裝的鉤子則處在鏈的末尾,。要實(shí)現(xiàn)Win32的系統(tǒng)鉤子,首先要調(diào)用SDK中的API函數(shù)SetWindowsHookEx來(lái)安裝這個(gè)鉤子函數(shù),,其原型是: HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId); 其中,,第一個(gè)參數(shù)是鉤子的類型,常用的有WH_MOUSE,、WH_KEYBOARD,、WH_GETMESSAGE等;第二個(gè)參數(shù)是鉤子函數(shù)的地址,,當(dāng)鉤子鉤到任何消息后便調(diào)用這個(gè)函數(shù),;第三個(gè)參數(shù)是鉤子函數(shù)所在模塊的句柄;第四個(gè)參數(shù)是鉤子相關(guān)函數(shù)的ID用以指定想讓鉤子去鉤哪個(gè)線程,,為0時(shí)則攔截整個(gè)系統(tǒng)的消息此時(shí)為全局鉤子,。如果指定確定的線程,,即為線程專用鉤子。 全局鉤子函數(shù)必須包含在DLL(動(dòng)態(tài)鏈接庫(kù))中,,而線程專用鉤子則可包含在可執(zhí)行文件中,。得到控制權(quán)的鉤子函數(shù)在處理完消息后,可以調(diào)用另外一個(gè)SDK中的API函數(shù)CallNextHookEx來(lái)繼續(xù)傳遞該消息,。也可以通過(guò)直接返回TRUE來(lái)丟棄該消息,,阻止該消息的傳遞。 使用全局鉤子函數(shù)時(shí)需要以DLL為載體,,VC6中有三種形式的MFC DLL可供選擇,,即Regular statically linked to MFC
DLL(標(biāo)準(zhǔn)靜態(tài)鏈接MFC DLL)、Regular using the shared MFC DLL(標(biāo)準(zhǔn)動(dòng)態(tài)鏈接MFC
DLL)以及Extension MFC DLL(擴(kuò)展MFC
DLL),。第一種DLL在編譯時(shí)把使用的MFC代碼鏈接到DLL中,,執(zhí)行程序時(shí)不需要其他MFC動(dòng)態(tài)鏈接類庫(kù)的支持,但體積較大,;第二種DLL在運(yùn)行時(shí)動(dòng)態(tài)鏈接到MFC類庫(kù),,因而體積較小,但卻依賴于MFC動(dòng)態(tài)鏈接類庫(kù)的支持,;這兩種DLL均可被MFC程序和Win32程序使用,。第三種DLL的也是動(dòng)態(tài)連接,但做為MFC類庫(kù)的擴(kuò)展,,只能被MFC程序使用,。 三、Win32 DLL Win32 DLL的入口和出口函數(shù)都是DLLMain這同Win16 DLL是有區(qū)別的,。只要有進(jìn)程或線程載入和卸載DLL時(shí),,都會(huì)調(diào)用該函數(shù),其原型是: BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved); 其中,,第一個(gè)參數(shù)表示DLL的實(shí)例句柄,;第三個(gè)參數(shù)系統(tǒng)保留;第二個(gè)參數(shù)指明了當(dāng)前調(diào)用該動(dòng)態(tài)連接庫(kù)的狀態(tài),,它有四個(gè)可能的值:DLL_PROCESS_ATTACH(進(jìn)程載入),、DLL_THREAD_ATTACH(線程載入)、DLL_THREAD_DETACH(線程卸載),、DLL_PROCESS_DETACH(進(jìn)程卸載),。在DLLMain函數(shù)中可以通過(guò)對(duì)傳遞進(jìn)來(lái)的這個(gè)參數(shù)的值進(jìn)行判別,根據(jù)不同的參數(shù)值對(duì)DLL進(jìn)行必要的初始化或清理工作,。由于在Win32環(huán)境下,,所有進(jìn)程的空間都是相互獨(dú)立的,這減少了應(yīng)用程序間的相互影響,,但大大增加了編程的難度,。當(dāng)進(jìn)程在動(dòng)態(tài)加載DLL時(shí),,系統(tǒng)自動(dòng)把DLL地址映射到該進(jìn)程的私有空間,而且也復(fù)制該DLL的全局?jǐn)?shù)據(jù)的一份拷貝到該進(jìn)程空間,,每個(gè)進(jìn)程所擁有的相同的DLL的全局?jǐn)?shù)據(jù)其值卻并不一定是相同的,。當(dāng)DLL內(nèi)存被映射到進(jìn)程空間中,每個(gè)進(jìn)程都有自己的全局內(nèi)存拷貝,,加載DLL的每一個(gè)新的進(jìn)程都重新初始化這一內(nèi)存區(qū)域,也就是說(shuō)進(jìn)程不能再共享DLL,。因此,,在Win32環(huán)境下要想在多個(gè)進(jìn)程中共享數(shù)據(jù),就必須進(jìn)行必要的設(shè)置,。一種方法便是把這些需要共享的數(shù)據(jù)單獨(dú)分離出來(lái),,放置在一個(gè)獨(dú)立的數(shù)據(jù)段里,并把該段的屬性設(shè)置為共享,,建立一個(gè)內(nèi)存共享的DLL,。
四、全局共享數(shù)據(jù)的實(shí)現(xiàn) 可以用#pragma data_seg建立一個(gè)新的數(shù)據(jù)段并定義共享數(shù)據(jù),,其具體格式為: #pragma data_seg ("shareddata") HWND sharedwnd=NULL;//共享數(shù)據(jù) #pragma data_seg() 所有在data_seg pragmas語(yǔ)句之間聲明的變量都將在shareddata段中,。僅定義一個(gè)數(shù)據(jù)段還不能達(dá)到共享數(shù)據(jù)的目的,還要告訴編譯器該段的屬性,,有兩種方法可以實(shí)現(xiàn)該目的(其效果是相同的),,一種方法是在.DEF文件中加入如下語(yǔ)句: SETCTIONS shareddata READ WRITE SHARED 另一種方法是在項(xiàng)目設(shè)置鏈接選項(xiàng)中加入如下語(yǔ)句: /SECTION:shareddata,rws 五、鼠標(biāo)鉤子程序示例 本示例程序用到全局鉤子函數(shù),,程序分兩部分:可執(zhí)行程序MouseDemo和動(dòng)態(tài)連接庫(kù)MouseHook,。首先編制MFC擴(kuò)展動(dòng)態(tài)連接庫(kù)MouseHook.dll: (一)選擇MFC AppWizard(DLL)創(chuàng)建項(xiàng)目Mousehook; (二)選擇MFC Extension DLL(MFC擴(kuò)展DLL)類型,; (三)通過(guò)Project菜單的AddToProject子菜單的"New…"添加頭文件MouseHook.h,。 (四)在頭文件中建立鉤子類: class AFX_EXT_CLASS CMouseHook:public CObject { public: CMouseHook(); //鉤子類的構(gòu)造函數(shù) ~CMouseHook(); //鉤子類的析構(gòu)函數(shù) BOOL StartHook(HWND hWnd); //安裝鉤子函數(shù) BOOL StopHook(); //卸載鉤子函數(shù) }; (五)在MouseHook.cpp文件中加入#include"MouseHook.h"語(yǔ)句; (六)加入全局共享數(shù)據(jù)變量: #pragma data_seg("mydata") HWND glhPrevTarWnd=NULL; //上次鼠標(biāo)所指的窗口句柄 HWND glhDisplayWnd=NULL; //顯示目標(biāo)窗口標(biāo)題編輯框的句柄 HHOOK glhHook=NULL; //安裝的鼠標(biāo)勾子句柄 HINSTANCE glhInstance=NULL; //DLL實(shí)例句柄 #pragma data_seg() (七)在DEF文件中定義段屬性: SECTIONS mydata READ WRITE SHARED (八)在主文件MouseHook.cpp的DllMain函數(shù)中加入保存DLL實(shí)例句柄的語(yǔ)句: extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { UNREFERENCED_PARAMETER(lpReserved); if (dwReason == DLL_PROCESS_ATTACH) { if (!AfxInitExtensionModule(MouseHookDLL, hInstance)) return 0; new CDynLinkLibrary(MouseHookDLL); glhInstance=hInstance; //插入保存DLL實(shí)例句柄 } else if (dwReason == DLL_PROCESS_DETACH) { AfxTermExtensionModule(MouseHookDLL); } return 1; // ok } 這個(gè)函數(shù)最重要的部分是調(diào)用AfxInitExtensionModule(),,它初始化DLL使它在MFC框架中正確的工作,。它需要傳遞給DllMain()的DLL實(shí)例句柄和AFX_EXTENSION_MODULE結(jié)構(gòu),結(jié)構(gòu)中存在著對(duì)MFC有用的信息,。 (九) 類CMouseHook的成員函數(shù)的具體實(shí)現(xiàn): Cmousehook::Cmousehook() //類構(gòu)造函數(shù) { } Cmousehook::~Cmousehook() //類析構(gòu)函數(shù) { stophook(); } BOOL Cmousehook::starthook(HWND hWnd) //安裝鉤子并設(shè)定接收顯示窗口句柄 { BOOL bResult=FALSE; glhHook=SetWindowsHookEx(WH_MOUSE,MouseProc,glhInstance,0); if(glhHook!=NULL) bResult=TRUE; glhDisplayWnd=hWnd; //設(shè)置顯示目標(biāo)窗口標(biāo)題編輯框的句柄 return bResult; } BOOL Cmousehook::stophook() //卸載鉤子 { BOOL bResult=FALSE; if(glhHook) { bResult= UnhookWindowsHookEx(glhHook); if(bResult) { glhPrevTarWnd=NULL; glhDisplayWnd=NULL;//清變量 glhHook=NULL; } } return bResult; } (十) 鉤子函數(shù)的實(shí)現(xiàn) LRESULT WINAPI MouseProc(int nCode,WPARAM wparam,LPARAM lparam) { LPMOUSEHOOKSTRUCT pMouseHook=(MOUSEHOOKSTRUCT FAR *) lparam; if (nCode>=0) { HWND glhTargetWnd=pMouseHook->hwnd; //取目標(biāo)窗口句柄 HWND ParentWnd=glhTargetWnd; while (ParentWnd !=NULL) { glhTargetWnd=ParentWnd; ParentWnd=GetParent(glhTargetWnd); //取應(yīng)用程序主窗口句柄 } if(glhTargetWnd!=glhPrevTarWnd) { char szCaption[100]; GetWindowText(glhTargetWnd,szCaption,100); //取目標(biāo)窗口標(biāo)題 if(IsWindow(glhDisplayWnd)) SendMessage(glhDisplayWnd,WM_SETTEXT,0,(LPARAM)(LPCTSTR)szCaption); glhPrevTarWnd=glhTargetWnd; //保存目標(biāo)窗口 } } return CallNextHookEx(glhHook,nCode,wparam,lparam); //繼續(xù)傳遞消息 } 編譯完成便可得到運(yùn)行時(shí)所需的鼠標(biāo)鉤子的動(dòng)態(tài)連接庫(kù)MouseHook.dll和鏈接時(shí)用到的MouseHook.lib,。 六、集成 下面新建一調(diào)用鼠標(biāo)鉤子動(dòng)態(tài)連接庫(kù)的鉤子可執(zhí)行程序: (一) 用MFC的AppWizard(EXE)創(chuàng)建項(xiàng)目MouseDemo,; (二) 選擇"基于對(duì)話應(yīng)用",,其余幾步均為確省,; (三) 在對(duì)話框上加入一個(gè)編輯框IDC_EDIT1,; (四) 在MouseDemo.h中加入對(duì)Mousehook.h的包含語(yǔ)句:#Include"Mousehook.h",; (五) 在CMouseDemoDlg.h的CMouseDemoDlg類定義中添加私有數(shù)據(jù)成員:CMouseHook m_hook; (六) 在OnInitDialog函數(shù)的"TODO注釋"后添加: CWnd * pwnd=GetDlgItem(IDC_EDIT1); //取得編輯框的類指針 m_hook.StartHook(pwnd->GetSafeHwnd()); //取得編輯框的窗口句柄并安裝鉤子 (七)鏈接DLL庫(kù),即把Mousehook.lib加入到項(xiàng)目設(shè)置鏈接標(biāo)簽中,; (八)把MouseHook.h和MouseHook.lib復(fù)制到MouseDemo工程目錄中,,MouseHook.dll復(fù)制到Debug目錄下。編譯執(zhí)行程序即可,。當(dāng)鼠標(biāo)滑過(guò)窗口時(shí)便會(huì)在編輯框中將此窗口的標(biāo)題顯示出來(lái),。 結(jié)論:
系統(tǒng)鉤子具有相當(dāng)強(qiáng)大的功能,通過(guò)這種技術(shù)可以對(duì)幾乎所有的Windows系統(tǒng)消息進(jìn)行攔截,、監(jiān)視,、處理。這種技術(shù)可以廣泛應(yīng)用于各種軟件,,尤其是需要有監(jiān)控,、自動(dòng)記錄等對(duì)系統(tǒng)進(jìn)行監(jiān)測(cè)功能的軟件。本程序只對(duì)鼠標(biāo)消息進(jìn)行攔截,,相應(yīng)的也可以在Win32環(huán)境下對(duì)鍵盤(pán),、端口等應(yīng)用此技術(shù)完成特定的功能。 |
|
來(lái)自: talentfirst1 > 《我的圖書(shū)館》