日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > windows >内容正文

windows

我写的几篇技术文章之一:Windows消息拦截技术的应用

發(fā)布時(shí)間:2023/12/10 windows 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 我写的几篇技术文章之一:Windows消息拦截技术的应用 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Windows消息攔截技術(shù)的應(yīng)用<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

民航合肥空管中心 周毅

?

一、前 言

眾所周知,Windows程式的運(yùn)行是依靠發(fā)生的事件來驅(qū)動(dòng)。換句話說,程式不斷等待一個(gè)消息的發(fā)生,然后對(duì)這個(gè)消息的類型進(jìn)行判斷,再做適當(dāng)?shù)奶幚怼L幚硗甏舜蜗⒑笥只氐降却隣顟B(tài)。從上面對(duì)Windows程式運(yùn)行機(jī)制的分析不難發(fā)現(xiàn),消息在用戶與程式之間進(jìn)行交流時(shí)起了一種中間“語(yǔ)言”的作用。在程式中接收和處理消息的主角是窗口,它通過消息泵接收消息,再通過一個(gè)窗口過程對(duì)消息進(jìn)行相應(yīng)的處理。

消息攔截的實(shí)現(xiàn)是在窗口過程處理消息之前攔截到消息并做相關(guān)處理后再傳送給原窗口過程。通常情況下,程序員可以在窗口過程中處理接收到的消息,這就要求窗口過程必須在開發(fā)程序時(shí)完成,但是在一些應(yīng)用中常常需要獲取和處理另外應(yīng)用程序或其它單元模塊中的消息,實(shí)現(xiàn)此類功能的技術(shù)也就本文要討論的主題――消息攔截技術(shù)。

?

二、理解Windows消息機(jī)制

在深入探討消息攔截技術(shù)實(shí)現(xiàn)原理之前,讓我們先來溫習(xí)一下Windows消息機(jī)制原理知識(shí)。

1、? 消息的產(chǎn)生

消息作為程序與外界交流的“語(yǔ)言”,它的產(chǎn)生自然來自外界,但這里所說的外界,不只是簡(jiǎn)單的指程序之外或軟件系統(tǒng)之外,而是泛指消息處理模塊之外的模塊、Windows系統(tǒng)、其它應(yīng)用程序以及硬件等。通常根據(jù)消息產(chǎn)生的方式將其分為兩大類,即硬件消息和軟件消息。硬件消息,常指由硬件裝置所產(chǎn)生的事件(如鼠標(biāo)或鍵盤被按下),放在系統(tǒng)消息隊(duì)列(System Queue)中,再由系統(tǒng)消息處理機(jī)構(gòu)將消息發(fā)送給應(yīng)用程序消息隊(duì)列中。軟件消息,常指由Windows系統(tǒng)或其它應(yīng)用程序發(fā)送的信息,它直接放入應(yīng)用程序消息隊(duì)列(Application Queue)中,再由應(yīng)用程序消息處理機(jī)構(gòu)將消息傳遞給相應(yīng)的窗口。

2、? 消息的組成

一個(gè)消息由一個(gè)消息名稱(UINT),和兩個(gè)參數(shù)(WPARAMLPARAM)。當(dāng)用戶進(jìn)行了輸入或是窗口的狀態(tài)發(fā)生改變時(shí)系統(tǒng)都會(huì)發(fā)送消息到某一個(gè)窗口。例如當(dāng)菜單轉(zhuǎn)中之后會(huì)有WM_COMMAND消息發(fā)送,WPARAM的高字中(HIWORD(wParam))是命令的ID號(hào),對(duì)菜單來講就是菜單ID。當(dāng)然用戶也可以定義自己的消息名稱,也可以利用自定義消息來發(fā)送通知和傳送數(shù)據(jù)。

3、? 消息的接收者

一個(gè)消息必須由一個(gè)窗口接收。在窗口過程(WNDPROC)中可以對(duì)消息進(jìn)行分析,對(duì)應(yīng)用程序要求處理的消息進(jìn)行相應(yīng)的處理工作,對(duì)于那么不需要應(yīng)用程序處理的消息可簡(jiǎn)單的調(diào)用缺省處理。例如你希望對(duì)菜單選擇進(jìn)行處理那么你可以定義對(duì)WM_COMMAND進(jìn)行處理的代碼,如果希望在窗口中進(jìn)行圖形輸出就必須對(duì)WM_PAINT進(jìn)行處理。

4、? 消息的處理

窗口接收到發(fā)送給自己的消息后,將消息結(jié)構(gòu)作為參數(shù)調(diào)用窗口過程對(duì)消息進(jìn)行相應(yīng)的處理。可以將窗口過程看作消息處理代碼的集合,窗口過程函數(shù)的原型為:

long FAR PASCAL WndProc(HWND hWnd,WORD message,WORD wParam,LONG lParam);
??
其中,hWnd為窗口句柄,message為消息名稱,wParam,lParam為兩個(gè)參數(shù)。

?? Windows中,應(yīng)用程序不直接調(diào)用任何窗口函數(shù),而是等待Windows調(diào)用窗口函數(shù),請(qǐng)求完成任務(wù)或返回信息。為保證Windows調(diào)用這個(gè)窗口函數(shù),這個(gè)函數(shù)必須先向Windows登記,然后在Windows實(shí)施相應(yīng)操作時(shí)回調(diào),所以窗口函數(shù)又稱為回調(diào)函數(shù)。WndProc是一個(gè)主回調(diào)函數(shù),Windows至少有一個(gè)回調(diào)函數(shù)。它是在應(yīng)用程序進(jìn)行窗口類注冊(cè)時(shí)向Windows登記的。

?

三、利用鉤子(Hook)攔截消息

1、 何為鉤子(Hook)?

鉤子(Hook)機(jī)制允許應(yīng)用程序截獲處理window消息或特定事件。與DOS中斷截獲處理機(jī)制有類似之處。鉤子是Windows消息處理機(jī)制的一個(gè)平臺(tái),應(yīng)用程序可以在上面設(shè)置子程以監(jiān)視指定窗口的某種消息,而且所監(jiān)視的窗口可以是其他進(jìn)程所創(chuàng)建的。當(dāng)消息到達(dá)后,鉤子可以在目標(biāo)窗口處理函數(shù)之前處理它并且可以阻止消息的傳遞。每一個(gè)鉤子都有一個(gè)與之相關(guān)聯(lián)的指針列表,稱之為鉤子鏈表,該鏈表中的指針指向這個(gè)鉤子的各個(gè)處理子程。鉤子的種類很多,每種鉤子可以攔截并處理相應(yīng)種類的消息。當(dāng)鉤子所監(jiān)視的消息出現(xiàn)時(shí),Windows調(diào)用鏈表中的第一個(gè)鉤子子程,第一個(gè)過程完成后將消息傳遞鏈表中的下一個(gè)鉤子子程,直至鏈表中所有鉤子子程都執(zhí)行完成(注意:如果在其中有一個(gè)鉤子在執(zhí)行完成前不執(zhí)行消息傳遞,其后面的鉤子過程和原窗口過程都不會(huì)再接收到消息。)后將消息返回給窗口過程。

2、 鉤子子程函數(shù)

鉤子子程是一個(gè)應(yīng)用程序定義的回調(diào)函數(shù)。用以監(jiān)視系統(tǒng)或某一特定類型的事件,這些事件可以是與某一特定線程關(guān)聯(lián)的,也可以是系統(tǒng)中所有線程的事件。其函數(shù)原型為:

?

LRESULT?CALLBACK?HookProc ?(??int?nCode,?WPARAM?wParam,?LPARAM?lParam );

?

其中,nCode參數(shù)是Hook代碼,Hook子程使用這個(gè)參數(shù)來確定任務(wù)。這個(gè)參數(shù)的值依賴于Hook類型,每一種Hook都有自己的Hook代碼特征字符集。 Windows系統(tǒng)提供了多種類型的鉤子,每一種類型的Hook可以使應(yīng)用程序能夠監(jiān)視不同類型的系統(tǒng)消息處理機(jī)制。

wParamlParam參數(shù)的值依賴于Hook代碼,但是它們的典型值是包含了關(guān)于發(fā)送或者接收消息的信息。

3、鉤子的安裝與卸載

鉤子的安裝是通過SDK API SetWindowsHookEx()來實(shí)現(xiàn)的,它將鉤子子程安裝到系統(tǒng)鉤子鏈表中。其函數(shù)原型

?

HHOOK?SetWindowsHookEx(?int?idHook,HOOKPROC?lpfn,HINSTANCE?hMod, DWORD?dwThreadId?);

?

其中,idHook是指鉤子的類型。表一中列出部分鉤子的類型及其說明。

(表一)

類型

說明

WH_CALLWNDPROC

系統(tǒng)在消息發(fā)送到接收窗口過程之前調(diào)用此子程

WH_CALLWNDPROCRET?Hooks

在窗口過程處理完消息之后調(diào)用此子程

WH_GETMESSAGE

監(jiān)視從GetMessage?/?PeekMessage函數(shù)返回的消息

WH_KEYBOARD

監(jiān)視輸入到消息隊(duì)列中的鍵盤消息

WH_MOUSE

監(jiān)視輸入到消息隊(duì)列中的鼠標(biāo)消息

限于篇幅,其它消息類型就不一一列出了。有關(guān)內(nèi)容可參見MSDN

?

lpfn是指鉤子子程的地址指針。如果dwThreadId參數(shù)為0?或是一個(gè)由別的進(jìn)程創(chuàng)建的線程的標(biāo)識(shí),lpfn 須指向DLL中的鉤子子程。除此以外,lpfn可以指向當(dāng)前進(jìn)程的一段鉤子子程代碼。?

?hMod是指應(yīng)用程序?qū)嵗木浔?biāo)識(shí)包含lpfn所指的子程的DLL。如果dwThreadId?標(biāo)識(shí)當(dāng)前進(jìn)程創(chuàng)建的一個(gè)線程,而且子程代碼位于當(dāng)前進(jìn)程,hMod必須為NULL?

dwThreadId是指與安裝的鉤子子程相關(guān)聯(lián)的線程的標(biāo)識(shí)符,如果為0,鉤子子程與所有的線程關(guān)聯(lián)。

函數(shù)成功則返回鉤子的句柄,失敗返回NULL

?

鉤子在使用完之后需要用UnHookWindowsHookEx()卸載,否則會(huì)造成麻煩。卸載鉤子比較簡(jiǎn)單,UnHookWindowsHookEx()只有一個(gè)參數(shù)。函數(shù)原型如下:

UnHookWindowsHookEx??(?HHOOK?hhk??)

其中,參數(shù)hhkSetWindowsHookEx()函數(shù)返回鉤子句柄,所以設(shè)計(jì)程序時(shí)一定要保存好這個(gè)句柄,以便卸載時(shí)使用。函數(shù)成功返回TRUE,否則返回FALSE

?

4、系統(tǒng)鉤子與線程鉤子

Windows系統(tǒng)根據(jù)鉤子監(jiān)視事件的范圍將鉤子分為系統(tǒng)鉤子(全局鉤子)和線程鉤子(局部鉤子)兩種。由SetWindowsHookEx()函數(shù)的最后一個(gè)參數(shù)決定了此鉤子是系統(tǒng)鉤子還是線程鉤子。線程勾子用于監(jiān)視指定線程的事件消息。線程勾子一般在當(dāng)前線程或者當(dāng)前線程派生的線程內(nèi)。 系統(tǒng)勾子監(jiān)視系統(tǒng)中的所有線程的事件消息。因?yàn)橄到y(tǒng)勾子會(huì)影響系統(tǒng)中所有的應(yīng)用程序,所以勾子函數(shù)必須放在獨(dú)立的動(dòng)態(tài)鏈接庫(kù)(DLL)?中。系統(tǒng)自動(dòng)將包含"鉤子回調(diào)函數(shù)"DLL映射到受鉤子函數(shù)影響的所有進(jìn)程的地址空間中,即將這個(gè)DLL注入了那些進(jìn)程。

?

5、? 鉤子的實(shí)現(xiàn)

本文的實(shí)例實(shí)現(xiàn)攔截記事本(NotePad.exe)程序的WM_CHAR消息的功能。如讀者想實(shí)現(xiàn)其它功能,可直接在鉤子子程函數(shù)中加入代碼。

(1)、選擇MFC?AppWizard(DLL)創(chuàng)建項(xiàng)目NotePadhook并選擇MFC?Extension?DLL(共享MFC拷貝)類型。

(2)、創(chuàng)建NotePadHook.h文件,在其中建立鉤子類:?

 class?AFX_EXT_CLASS?CNotePadHook:public?CObject?

 { 

 public:

 CNotePadHook();?//鉤子類的構(gòu)造函數(shù)

 ~CNotePadHook();?//鉤子類的析構(gòu)函數(shù)

 BOOL?StartHook(HWND?hWnd);? //安裝鉤子函數(shù)?

 BOOL?StopHook();?卸載鉤子函數(shù)

 };?

(3)、在NotePadHook.cpp中加入#include “NotePadHook.h”

(4)、在NotePadHook.cpp中加入共享數(shù)據(jù)段:

#pragma data_seg("sharedata")? //共享數(shù)據(jù)段,段內(nèi)的變量可被鉤子所有實(shí)例共享。

HHOOK glhHook=NULL;? //鉤子句柄。

HINSTANCE glhInstance=NULL;? //DLL實(shí)例句柄。

#pragma data_seg()?

(5)、僅定義一個(gè)數(shù)據(jù)段還不能達(dá)到共享數(shù)據(jù)的目的,還要告訴編譯器該段的屬性。要在.DEF文件中設(shè)置段的屬性,打開.DEF文件加入如下代碼:

SETCTIONS?

sharedata?READ?WRITE?SHARED?

(6)、在主文件NotePadHook.cppDllMain函數(shù)中加入保存DLL實(shí)例句柄:

DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)

{

????? //如果使用lpReserved參數(shù)則刪除下面這行?

????? UNREFERENCED_PARAMETER(lpReserved);

?

????? if (dwReason == DLL_PROCESS_ATTACH)

????? {

????? ????? TRACE0("NOtePadHOOK.DLL Initializing!\n");

????? ????? ?//擴(kuò)展DLL僅初始化一次?

?????????? if (!AfxInitExtensionModule(NotePadHookDLL, hInstance))

?????????? ????? return 0;

????? ????? new CDynLinkLibrary(NotePadHookDLL);

//DLL加入動(dòng)態(tài)MFC類庫(kù)中?

????? ????? glhInstance = hInstance;

?????? //插入保存DLL實(shí)例句柄?

????? }

????? else if (dwReason == DLL_PROCESS_DETACH)

????? {

????? ????? TRACE0("NotePadHOOK.DLL Terminating!\n");

?????????? //終止這個(gè)鏈接庫(kù)前調(diào)用它?

????? ????? AfxTermExtensionModule(NotePadHookDLL);

????? }

????? return 1;??

}

(7)、類CNotePadHook的成員函數(shù)的具體實(shí)現(xiàn):

CNotePadHook::CNotePadHook(){}

CNotePadHook::~CNotePadHook(){ StopHook(); }

BOOL CNotePadHook::StartHook(HWND hWnd) //安裝鉤子。

{

? BOOL bResult=FALSE;

glhHook=SetWindowsHookEx(WH_CALLWNDPROC,NotePadProc,glhInstance,0);

? if(glhHook!=NULL) bResult=TRUE;

return bResult;

}

CNotePadHook::StopHook()

{

?? BOOL bResult = FALSE;

?? if(glhHook){

????? bResult=UnhookWindowsHookEx(glhHook);

?? if(bResult)? glhHook=NULL;

?? return bResult;

}

(8)、鉤子子程的實(shí)現(xiàn):

LRESULT?WINAPI?NotePadProc(int?nCode,WPARAM?wparam,LPARAM?lparam)?

{

? PCWPSTRUCT pcs = NULL;

pcs = (PCWPSTRUCT)lParam;

????? if( pcs && pcs->hwnd!=NULL )

????? {

?????????? ? TCHAR szClass[256];

?????????? ? GetClassName(pcs->hwnd ,szClass,255);//獲得攔截的窗口類名。

?????????? ? if( strcmp(szClass,"Notepad")==0)

?????????? ? {

?????????? ???? if( pcs->message == WM_CHAR )

?????????? ????? ?{

?????????? ????? ??? AfxMessageBox("HOOK NOTEPAD WM_CHAR OK!!!");

?????????? ????? ?}

????? ????? ? }

}

??? return CallNextHookEx(glhHook,nCode,wParam,lParam);//繼續(xù)傳遞消息。

}

?

(9)、編譯項(xiàng)目生成NotePadHook.dll

?雖然已經(jīng)完成了鉤子類,但還不能實(shí)現(xiàn)鉤子功能。我們必須寫一個(gè)程序來啟動(dòng)鉤子,將鉤子DLL注入其它程序的內(nèi)存空間并將鉤子加入到系統(tǒng)鉤子鏈表中。由于限于篇幅,在本文就不具體講述鉤子啟動(dòng)程序的實(shí)例,只將編寫啟動(dòng)程序應(yīng)注意的事項(xiàng)說明如下:

(1)、將NotePadHook項(xiàng)目中Debug\NotePadHook.lib加入到項(xiàng)目設(shè)置鏈接標(biāo)簽中。

(2)、將NotePadHook項(xiàng)目中NotePadHook.h文件includestdafx.h

(3)、首先需要?jiǎng)?chuàng)建一個(gè)CNotePadHook類實(shí)例,啟動(dòng)鉤子時(shí)調(diào)用類成員StartHook(),卸載鉤子時(shí)調(diào)用類成員StopHook()

?

四、利用窗口子類化(SubClass)攔截消息

前面已提及,每個(gè)窗口都有一個(gè)在它的窗口類中定義的窗口過程。該窗口過程處理每個(gè)發(fā)送到窗口的消息。如果想自己編寫窗口過程,修改它的行為是沒有問題的。但是,如果該窗口過程屬于別人,則將沒有源代碼進(jìn)行修改。例如,應(yīng)用程序中的每個(gè)按鈕,都是由系統(tǒng)提供的BUTTON窗口創(chuàng)建的,它有完全屬于自己的窗口過程。如果想改變?cè)摯翱诘耐庥^,則不能通過改變它的WM_PAINT處理函數(shù)來實(shí)現(xiàn),因?yàn)樗遣豢傻玫摹D敲?#xff0c;怎樣能改變這些按鈕的外觀,而無(wú)需重新編寫原來的控件呢?只要用自己的窗口過程的地址,替換窗口對(duì)象的初始窗口過程的地址即可。這種技術(shù)也是本節(jié)討論的主題 窗口子類化技術(shù)。

  1、窗口子類化原理

應(yīng)用程序在創(chuàng)建一個(gè)新窗口之前要向Windows系統(tǒng)注冊(cè)這個(gè)窗口的類,首先要填寫一個(gè)WNDCLASS結(jié)構(gòu),其中的結(jié)構(gòu)參數(shù)lpfnWndProc就是該類窗口函數(shù)的地址,接著調(diào)用RegisterClass()函數(shù)向Windows系統(tǒng)申請(qǐng)注冊(cè)這個(gè)窗口類。這時(shí)Windows會(huì)為其分配一塊內(nèi)存來存放該類的全部信息,這個(gè)內(nèi)存塊稱為窗口類內(nèi)存塊。

窗口子類化技術(shù)實(shí)際上就是改變窗口內(nèi)存塊中的有關(guān)參數(shù)。由于這種修改只涉及到一個(gè)窗口的窗口內(nèi)存塊,因此它不會(huì)影響到屬于同一窗口類的其它窗口的功能和表現(xiàn)。窗口子類化中最常見的是修改窗口內(nèi)存塊中的窗口函數(shù)地址(lpfnWndProc),使其指向一個(gè)新的窗口函數(shù),從而改變?cè)翱诤瘮?shù)的處理方法,以達(dá)到修改其窗口過程的目的。

2、窗口子類化的實(shí)現(xiàn)

窗口子類化實(shí)現(xiàn)的核心是改變窗口過程的地址,可以通過SDK API提供的幾個(gè)函數(shù)來實(shí)現(xiàn)。具體步驟如下:

a.編寫子類化窗口過程函數(shù)。該函數(shù)必須為標(biāo)準(zhǔn)的窗口過程函數(shù)格式即:?

  LRESULT?CALLBACK?SubClassWndProc?(?HWND?,?UINT?,?WPARAM?,?LPARAM?)?;?

??? 此函數(shù)的參數(shù)意義與前面講述的窗口過程函數(shù)參數(shù)類似。

b.調(diào)用GetWindowLong?(?hWnd?,?GWL_WNDPROC?)?函數(shù)獲得原窗口函數(shù)的地址并保存起來;其中參數(shù)hWnd為待子類化窗口句柄。

C.調(diào)用SetWIndowLong?(?hWnd?,?GWL_WNDPROC?,?SubClassWndProc?)?把窗口函數(shù)設(shè)置成子類化窗口函數(shù),完成窗口子類化。?

?

為了減少子類化過程中繁瑣的工作,MFC中提供了對(duì)子類化的支持,它簡(jiǎn)化了子類化過程,利用CWndSubClassWindows()函數(shù)來實(shí)現(xiàn)子類化。為了讓讀者對(duì)子類化過程有一個(gè)直觀的認(rèn)識(shí),下面將利用MFC實(shí)現(xiàn)對(duì)一個(gè)編輯(Edit)控件的子類化。

(1)、創(chuàng)建一個(gè)從MFC控件類CEdit派生的新控件類CSubEdit

(2)、添加CSubEdit::PreTranslateMessage(MSG* pMsg)

BOOL CSubEdit::PreTranslateMessage(MSG* pMsg)

{

?? if( pMsg->message==WM_KEYDOWN&&pMsg->wParam==VK_RETURN)

?? {

? //當(dāng)在Edit控件上按下回車鍵后

?…..

//限于篇幅處理內(nèi)容略。

return TRUE;

}

CEdit::PreTranslateMessage(pMsg);

}

(3)、在包含此控件的對(duì)話框類頭文件中控件變量類型從CEdit改為CSubEdit

(4)、在包含此控件的對(duì)話框類文件中對(duì)Edit控件進(jìn)行子類化,代碼如下:

 HWND HwND;

? GetDlgItem(IDC_SUB_EDIT,&hWnd);//其中IDC_SUB_EDIT是控件ID

 m_subEdit.SubclassWindow(hWnd); //m_subEdit為控件變量名。

?

五、小結(jié)

本文討論了實(shí)現(xiàn)消息攔截的兩種方法,其中鉤子技術(shù)用途廣泛,不僅可以實(shí)現(xiàn)對(duì)同進(jìn)程內(nèi)消息的攔截,而且還可以實(shí)現(xiàn)對(duì)另外進(jìn)程消息的攔截。而子類化技術(shù)主要用于實(shí)現(xiàn)對(duì)同一進(jìn)程單元模塊中的窗口消息的攔截。程序員可以根據(jù)實(shí)際應(yīng)用需求選擇其一來實(shí)現(xiàn)消息的擋截。

轉(zhuǎn)載于:https://www.cnblogs.com/biqing/archive/2004/05/20/10571.html

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的我写的几篇技术文章之一:Windows消息拦截技术的应用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。