小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Windows消息攔截技術(shù)的應(yīng)用

 guoliyan1 2012-02-16

Windows消息攔截技術(shù)的應(yīng)用  


一、前 言

眾所周知,Windows程式的運(yùn)行是依靠發(fā)生的事件來驅(qū)動(dòng)。換句話說,程式不斷等待一個(gè)消息的發(fā)生,然后對這個(gè)消息的類型進(jìn)行判斷,再做適當(dāng)?shù)奶幚?。處理完此次消息后又回到等待狀態(tài)。從上面對Windows程式運(yùn)行機(jī)制的分析不難發(fā)現(xiàn),消息在用戶與程式之間進(jìn)行交流時(shí)起了一種中間“語言”的作用。在程式中接收和處理消息的主角是窗口,它通過消息泵接收消息,再通過一個(gè)窗口過程對消息進(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)生

消息作為程序與外界交流的“語言”,它的產(chǎ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),對菜單來講就是菜單ID。當(dāng)然用戶也可以定義自己的消息名稱,也可以利用自定義消息來發(fā)送通知和傳送數(shù)據(jù)。

3、  消息的接收者

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

4、  消息的處理

窗口接收到發(fā)送給自己的消息后,將消息結(jié)構(gòu)作為參數(shù)調(diào)用窗口過程對消息進(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ù),請求完成任務(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)行窗口類注冊時(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ù)為或是一個(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ì)造成麻煩。卸載鉤子比較簡單,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)鏈接庫(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  //好像要改為SECTIONS  ,否則編譯有錯(cuò)誤

sharedata READ WRITE SHARED 

另外一種方法:

也可以在代碼中直接設(shè)置:
  #pragma data_seg("sharedata") 
..........
  #pragma data_seg() 
  #pragma comment(linker,"/SECTION:sharedata,RWS") 

(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類庫中 

            glhInstance = hInstance;

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

      }

      else if (dwReason == DLL_PROCESS_DETACH)

      {

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

           //終止這個(gè)鏈接庫前調(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)建的,它有完全屬于自己的窗口過程。如果想改變該窗口的外觀,則不能通過改變它的WM_PAINT處理函數(shù)來實(shí)現(xiàn),因?yàn)樗遣豢傻玫摹D敲?,怎樣能改變這些按鈕的外觀,而無需重新編寫原來的控件呢?只要用自己的窗口過程的地址,替換窗口對象的初始窗口過程的地址即可。這種技術(shù)也是本節(jié)討論的主題 窗口子類化技術(shù)。

 ?。?、窗口子類化原理

應(yīng)用程序在創(chuàng)建一個(gè)新窗口之前要向Windows系統(tǒng)注冊這個(gè)窗口的類,首先要填寫一個(gè)WNDCLASS結(jié)構(gòu),其中的結(jié)構(gòu)參數(shù)lpfnWndProc就是該類窗口函數(shù)的地址,接著調(diào)用RegisterClass()函數(shù)向Windows系統(tǒng)申請注冊這個(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ù),從而改變原窗口函數(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中提供了對子類化的支持,它簡化了子類化過程,利用CWndSubClassWindows()函數(shù)來實(shí)現(xiàn)子類化。為了讓讀者對子類化過程有一個(gè)直觀的認(rèn)識(shí),下面將利用MFC實(shí)現(xiàn)對一個(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)、在包含此控件的對話框類頭文件中控件變量類型從CEdit改為CSubEdit。

(4)、在包含此控件的對話框類文件中對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)對同進(jìn)程內(nèi)消息的攔截,而且還可以實(shí)現(xiàn)對另外進(jìn)程消息的攔截。而子類化技術(shù)主要用于實(shí)現(xiàn)對同一進(jìn)程單元模塊中的窗口消息的攔截。程序員可以根據(jù)實(shí)際應(yīng)用需求選擇其一來實(shí)現(xiàn)消息的擋截。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請遵守用戶 評(píng)論公約

    類似文章 更多