Win32全域性鉤子實現 (轉)
是建立在事件的機制上的,說穿了就是整個系統都是透過訊息的傳遞來實現的。而鉤子是Windows系統中非常重要的系統介面,用它可以截獲並處理送給其他應用的訊息,來完成普通應用程式難以實現的功能。鉤子的種類很多,每種鉤子可以截獲並處理相應的訊息,如鍵盤鉤子可以截獲鍵盤訊息,外殼鉤子可以擷取、啟動和關閉應用程式的訊息等。本文在VC5環境下實現了一個簡單的滑鼠鉤子程式,並對全域性鉤子的執行機制、Win32 DLL的特點、VC5環境下的MFC DLL以及共享資料等相關知識進行了簡單的闡述。
一.Win32全域性鉤子的執行機制
鉤子實際上是一個處理訊息的程式段,透過系統,把它掛入系統。每當特定的訊息發出,在沒有到達目的視窗前,鉤子程式就先捕獲該訊息,亦即鉤子先得到控制權。這時鉤子函式即可以加工處理(改變)該訊息,也可以不作處理而繼續傳遞該訊息,還可以強制結束訊息的傳遞。對每種型別的鉤子由系統來維護一個鉤子鏈,最近的鉤子放在鏈的開始,而最先安裝的鉤子放在最後,也就是後加入的先獲得控制權。要實現Win32的系統鉤子,必須呼叫SDK中的函式SetWindowsHookEx來安裝這個鉤子函式,這個函式的原型是HHOOK SetWindowsHookEx(int idHook,HOOKPROC lpfn,HINSTANCE hMod,D dwThreadId);,其中,第一個引數是鉤子的型別;第二個引數是鉤子函式的地址;第三個引數是包含鉤子函式的模組控制程式碼;第四個引數指定監視的執行緒。如果指定確定的執行緒,即為執行緒專用鉤子;如果指定為空,即為全域性鉤子。其中,全域性鉤子函式必須包含在DLL(動態連結庫)中,而執行緒專用鉤子還可以包含在可中。得到控制權的鉤子函式在完成對訊息的處理後,如果想要該訊息繼續傳遞,那麼它必須呼叫另外一個SDK中的API函式CallNextHookEx來傳遞它。鉤子函式也可以透過直接返回TRUE來丟棄該訊息,並阻止該訊息的傳遞。
二.Win32 DLL的特點
Win32 DLL與 Win16 DLL有很大的區別,這主要是由的設計思想決定的。一方面,在Win16 DLL中程式入口點函式和出口點函式(LibMain和WEP)是分別實現的;而在Win32 DLL中卻由同一函式DLLMain來實現。無論何時,當一個程式或執行緒載入和解除安裝DLL時,都要呼叫該函式,它的原型是BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved);,其中,第一個參數列示DLL的例項控制程式碼;第三個引數系統保留;這裡主要介紹一下第二個引數,它有四個可能的值:DLL_PROCESS_ATTACH(程式載入),DLL_THREAD_ATTACH(執行緒載入),DLL_THREAD_DETACH(執行緒解除安裝),DLL_PROCESS_DETACH(程式解除安裝),在DLLMain函式中可以對傳遞進來的這個引數的值進行判別,並根據不同的引數值對DLL進行必要的初始化或清理工作。舉個例子來說,當有一個程式載入一個DLL時,系統分派給DLL的第二個引數為DLL_PROCESS_ATTACH,這時,你可以根據這個引數初始化特定的資料。另一方面,在Win16環境下,所有應用程式都在同一地址空間;而在Win32環境下,所有應用程式都有自己的私有空間,每個程式的空間都是相互獨立的,這減少了應用程式間的相互影響,但同時也增加了程式設計的難度。大家知道,在Win16環境中,DLL的全域性資料對每個載入它的程式來說都是相同的;而在Win32環境中,情況卻發生了變化,當程式在載入DLL時,系統自動把DLL地址對映到該程式的私有空間,而且也複製該DLL的全域性資料的一份複製到該程式空間,也就是說每個程式所擁有的相同的DLL的全域性資料其值卻並不一定是相同的。因此,在Win32環境下要想在多個程式中共享資料,就必須進行必要的設定。亦即把這些需要共享的資料分離出來,放置在一個獨立的資料段裡,並把該段的屬性設定為共享。
三.VC5中MFC DLL的分類及特點
在VC5中有三種形式的MFC DLL(在該DLL中可以使用和繼承已有的MFC類)可供選擇,即Regular statically linked to MFC DLL(標準靜態連結MFC DLL)和Regular using the shared MFC DLL(標準動態連結MFC DLL)以及Extension MFC DLL(擴充套件MFC DLL)。第一種DLL的特點是,在編譯時把使用的MFC程式碼加入到DLL中,因此,在使用該程式時不需要其他MFC動態連結類庫的存在,但佔用空間比較大;第二種DLL的特點是,在執行時,動態連結到MFC類庫,因此減少了空間的佔用,但是在執行時卻依賴於MFC動態連結類庫;這兩種DLL既可以被MFC程式使用也可以被Win32程式使用。第三種DLL的特點類似於第二種,做為MFC類庫的擴充套件,只能被MFC程式使用。
四.在VC5中全域性共享資料的實現
在主檔案中,用#pragma data_seg建立一個新的資料段並定義共享資料,其具體格式為:
#pragma data_seg ("shareddata")
HWND sharedwnd=NULL;//共享資料
#pragma data_seg()
僅定義一個資料段還不能達到共享資料的目的,還要告訴該段的屬性,有兩種方法可以實現該目的(其效果是相同的),一種方法是在.DEF檔案中加入如下語句:
SETCTIONS
shareddata READ WRITE SHARED
另一種方法是在專案設定連結選項中加入如下語句:
/SECTION:shareddata,rws
五.具體實現步驟
由於全域性鉤子函式必須包含在動態連結庫中,所以本例由兩個程式體來實現。
1.建立鉤子Mousehook.DLL
(1)選擇MFC AppWizard(DLL)建立專案Mousehook;
(2)選擇MFC Extension DLL(共享MFC複製)型別;
(3)由於VC5沒有現成的鉤子類,所以要在專案目錄中建立Mousehook.h檔案,在其中建立鉤子類:
class AFX_EXT_CLASS Cmousehook:public C
{
public:
Cmousehook();
//鉤子類的建構函式
~Cmousehook();
//鉤子類的解構函式
BOOL starthook(HWND hWnd);
//安裝鉤子函式
BOOL stophook();
解除安裝鉤子函式
};
(4)在Mousehook.app檔案的頂部加入#include"Mousehook.h"語句;
(5)加入全域性共享資料變數:
#pragma data_seg("mydata")
HWND glhPrevTarWnd=NULL;
//上次滑鼠所指的視窗控制程式碼
HWND glhDisplayWnd=NULL;
//顯示目標視窗標題編輯框的控制程式碼
HHOOK glhHook=NULL;
//安裝的滑鼠勾子控制程式碼
HINSTANCE glhInstance=NULL;
//DLL例項控制程式碼
#pragma data_seg()
(6)在DEF檔案中定義段屬性:
SECTIONS
mydata READ WRITE SHARED
(7)在主檔案Mousehook.cpp的DllMain函式中加入儲存DLL例項控制程式碼的語句:
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
//如果使用lpReserved引數則刪除下面這行
UNREFERENCED_PARAMETER(lpReserved);
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("MOUSEHOOK.DLL Initializing!n");
//擴充套件DLL僅初始化一次
if (!AfxInitExtensionModule(MousehookDLL, hInstance))
return 0;
new CDynLinkLibrary(MousehookDLL);
//把DLL加入動態MFC類庫中
glhInstance=hInstance;
//插入儲存DLL例項控制程式碼
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("MOUSEHOOK.DLL Tenating!n");
//終止這個連結庫前呼叫它
AfxTermExtensionModule(MousehookDLL);
}
return 1;
}
(8)類Cmousehook的成員函式的具體實現:
Cmousehook::Cmousehook()
//類建構函式
{
}
Cmousehook::~Cmousehook()
//類解構函式
{
stophook();
}
BOOL Cmousehook::starthook(HWND hWnd)
//安裝鉤子並設定接收顯示視窗控制程式碼
{
BOOL bResult=FALSE;
glhHook=SetWindowsHookEx(WH_MOUSE,MouseProc,glhInstance,0);
if(glhHook!=NULL)
bResult=TRUE;
glhDisplayWnd=hWnd;
//設定顯示目標視窗標題編輯框的控制程式碼
return bResult;
}
BOOL Cmousehook::stophook()
//解除安裝鉤子
{
BOOL bResult=FALSE;
if(glhHook)
{
bResult= UnhookWindowsHookEx(glhHook);
if(bResult)
{
glhPrevTarWnd=NULL;
glhDisplayWnd=NULL;//清變數
glhHook=NULL;
}
}
return bResult;
}
(9)鉤子函式的實現:
LRESULT WINAPI MouseProc(int nCode,WPARAM wparam,LPARAM lparam)
{
LPMOUSEHOOKSTRUCT pMouseHook=(MOUSEHOOKSTRUCT FAR *) lparam;
if (nCode>=0)
{
HWND glhTargetWnd=pMouseHook->hwnd;
//取目標視窗控制程式碼
HWND ParentWnd=glhTargetWnd;
while (ParentWnd !=NULL)
{
glhTargetWnd=ParentWnd;
ParentWnd=GetParent(glhTargetWnd);
//取應用程式主視窗控制程式碼
}
if(glhTargetWnd!=glhPrevTarWnd)
{
char szCaption[100];
GetWindowText(glhTargetWnd,szCaption,100);
//取目標視窗標題
if(IsWindow(glhDisplayWnd))
SendMessage(glhDisplayWnd,WM_SETTEXT,0,(LPARAM)(LPCTSTR)szCaption);
glhPrevTarWnd=glhTargetWnd;
//儲存目標視窗
}
}
return CallNextHookEx(glhHook,nCode,wparam,lparam);
//繼續傳遞訊息
}
(10)編譯專案生成mousehook.dll。
2.建立鉤子可執行程式
(1)用MFC的AppWizard(EXE)建立專案Mouse;
(2)選擇“基於對話應用”並按下“完成”鍵;
(3)編輯對話方塊,刪除其中原有的兩個按鈕,加入靜態文字框和編輯框,用滑鼠右鍵點選靜態文字框,在彈出的選單中選擇“屬性”,設定其標題為“滑鼠所在的視窗標題”;
(4)在Mouse.h中加入對Mousehook.h的包含語句#Include"..MousehookMousehook.h";
(5)在CMouseDlg.h的CMouseDlg類定義中新增私有資料成員:
CMouseHook m_hook;//加入鉤子類作為資料成員
(6)修改CmouseDlg::OnInitDialog()函式:
BOOL CMouseDlg::OnInitDialog()
{
CDialog::OnInitDialog();
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX <0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
SetIcon(m_hIcon, TRUE);//Set big icon
SetIcon(m_hIcon, FALSE);//Set small icon
//TODO: Add extra initialization here
CWnd * pwnd=GetDlgItem(IDC_EDIT1);
//取得編輯框的類指標
m_hook.starthook(pwnd->GetSafeHwnd());
//取得編輯框的視窗控制程式碼並安裝鉤子
return TRUE;
//return TRUE unless you set the focus to a control
}
(7)連結DLL庫,即把..MousehookdeMousehook.lib加入到專案設定連結標籤中;
(8)編譯專案生成可執行檔案;
(9)把Mousehook.DLL複製到..mousedebug目錄中;
(10)先執行幾個可執行程式,然後執行Mouse.exe程式,把滑鼠在不同視窗中移動,在Mouse.exe程式視窗中的編輯框內將顯示出滑鼠所在的應用程式主視窗的標題。pcc
(作者地址:遼寧省鐵嶺縣委機要局 112000 )
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-988418/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 為XHR物件所有方法和屬性提供鉤子 全域性攔截AJAX物件
- vue[原始碼]生命週期鉤子的實現Vue原始碼
- React - Context API 維護全域性狀態,實現全域性元件通訊ReactContextAPI元件
- 這才是tapable裡面SyncLoopHook鉤子函式的實現OOPHook函式
- 實現elementUI表單的全域性驗證UI
- 透過鉤子函式+Traceid實現Flask鏈路追蹤函式Flask
- 用 Go 做了一個 Git WebHook 鉤子 實現自動部署GoGitWebHook
- 鴻蒙系統(HarmonyOS)全域性彈窗實現鴻蒙
- Java 實現系統全域性快捷鍵繫結Java
- 實現基於React的全域性提示元件ToastReact元件AST
- PostgreSQL 原始碼解讀(249)- 實現簡單的鉤子函式SQL原始碼函式
- React專案實現全域性 loading 以及錯誤提示React
- Vue3 如何實現全域性異常處理?Vue
- 全域性元件實現遞迴樹,避免迴圈引用元件遞迴
- Sentinel全域性Feign預設熔斷設計實現
- 完美實現賬戶踢出時的全域性彈窗
- polipo/privoxy 實現 Linux 系統全域性/自動代理Linux
- spring boot+自定義 AOP 實現全域性校驗Spring Boot
- 用 git 鉤子,檢測程式碼規範性(eslint、standard)GitEsLint
- React Hooks 鉤子特性ReactHook
- 新建鉤子檔案
- PHP系列之鉤子PHP
- JS模擬瀏覽器全域性搜尋功能實現JS瀏覽器
- Raspberry PI:Shadowsocks+Polipo實現全域性科學上網
- 微信小程式實現全域性搜尋程式碼高亮微信小程式
- 轉載:cnpm全域性安裝(淘寶映象)NPM
- Vue 生命週期鉤子Vue
- vue-router的鉤子Vue
- 如何實現簡單的定時全域性唯一任務?
- Java使用雪花演算法實現生成全域性唯一idJava演算法
- vue定義全域性變數和全域性方法Vue變數
- duxapp放棄了redux,在duxapp中區域性、全域性狀態的實現方案APPRedux
- 如何在分散式架構下完美實現“全域性資料一致性”?分散式架構
- JVM鉤子函式的使用JVM函式
- Vue的鉤子函式[路由導航守衛、keep-alive、生命週期鉤子]Vue函式路由Keep-Alive
- Thinkphp實戰利用鉤子使用行為擴充套件 (Hook)PHP套件Hook
- uniapp 全域性檢查登陸並跳轉函式APP函式
- 框架(frameset),全域性屬性框架
- flutter好用的輪子推薦六-超好用的全域性toastFlutterAST