當前位置:名人名言大全網 - 短信平臺 - WINDOWS消息處理過程

WINDOWS消息處理過程

壹、 引言

二、Windows消息機制的概念

1、DOS與Windows驅動機制的區別

2、消息

3、消息的來源

4、Windows的消息系統的組成

5、消息的響應

三、Windows消息機制要點

1. 窗口過程

2 消息類型

3消息隊列(Message Queues)

4 隊列消息和非隊列消息

5 Windows消息函數

6消息死鎖( Message Deadlocks

7 BroadcastSystemMessage

四、MFC消息機制

1.MFC框架下,接收處理來自Windows消息的過程

2.MFC內部消息處理方式

壹、 引言

在 C++程序架構 壹文中,我們看到,程序是由壹些層次和模塊組成的,那麽,這些模塊之間, 以及妳的程序和windows 之間,是如何傳遞信息呢?在windows 的平臺上,傳遞信息是由 windows message 消息機制來負責的,這是Windows 的核心部分。

消息包括數據和指令。

二、Windows消息機制的概念

1、DOS與Windows驅動機制的區別

1)DOS是過程驅動的。

傳統的MS-DOS程序主要采用順序的。關聯的、過程驅動的程序設計方法。壹個過程是壹系列預先定義好的操作序列的組合,它具有壹定的開頭、中間過程和結束。程序直接控制程序事件和過程的順序。這樣的程序設計方法是面向程序而不是面向用戶的,交互性差,用戶界面不夠友好,因為它強迫用戶按照某種不可更改的模式進行工作。它的基本模型如圖1.1所示。

2)Windows是事件(消息)驅動

事件驅動程序設計是壹種全新的程序設計方法,它不是由事件的順序來控制,而是由事件的發生來控制,而這種事件的發生是隨機的、不確定的,並沒有預定的順序,這樣就允許程序的的用戶用各種合理的順序來安排程序的流程。對於需要用戶交互的應用程序來說,事件驅動的程序設計有著過程驅動方法無法替代的優點。它是壹種面向用戶的程序設計方法,它在程序設計過程中除了完成所需功能之外,更多的考慮了用戶可能的各種輸入,並針對性的設計相應的處理程序。它是壹種“被動”式程序設計方法,程序開始運行時,處於等待用戶輸入事件狀態,然後取得事件並作出相應反應,處理完畢又返回並處於等待事件狀態。它的框圖如圖1.2所示:

2、消息

Windows系統是壹個事件驅動的OS,每壹個事件的發生都會產生壹個消息,我們通過消息來知道發生了什麽事件,了解事件,進而解決事件。什麽是消息呢?很難下壹個定義,下面從不同的幾個方面講解壹下:

1) 消息的組成:壹個消息由壹個消息名稱(UINT),和兩個參數(WPARAM,LPARAM)。當用戶進行了輸入或是窗口的狀態發生改變時系統都會發送消息到某壹個窗口。例如當菜單轉中之後會有WM_COMMAND消息發送,WPARAM的高字中(HIWORD(wParam))是命令的ID號,對菜單來講就是菜單ID。當然用戶也可以定義自己的消息名稱,也可以利用自定義消息來發送通知和傳送數據。

2)誰將收到消息:壹個消息必須由壹個窗口接收。在窗口的過程(WNDPROC)中可以對消息進行分析,對自己感興趣的消息進行處理。例如妳希望對菜單選擇進行處理那麽妳可以定義對WM_COMMAND進行處理的代碼,如果希望在窗口中進行圖形輸出就必須對WM_PAINT進行處理。

3)未處理的消息到那裏去了:M$為窗口編寫了默認的窗口過程,這個窗口過程將負責處理那些妳不處理消息。正因為有了這個默認窗口過程我們才可以利用Windows的窗口進行開發而不必過多關註窗口各種消息的處理。例如窗口在被拖動時會有很多消息發送,而我們都可以不予理睬讓系統自己去處理。

4)窗口句柄:說到消息就不能不說窗口句柄,系統通過窗口句柄來在整個系統中唯壹標識壹個窗口,發送壹個消息時必須指定壹個窗口句柄表明該消息由那個窗口接收。而每個窗口都會有自己的窗口過程,所以用戶的輸入就會被正確的處理。例如有兩個窗口***用壹個窗口過程代碼,妳在窗口壹上按下鼠標時消息就會通過窗口壹的句柄被發送到窗口壹而不是窗口二。

3、消息的來源

事件驅動圍繞著消息的產生與處理展開,壹條消息是關於發生的事件的消息。事件驅動是靠消息循環機制來實現的。也可以理解為消息是壹種報告有關事件發生的通知。

Windows應用程序的消息來源有壹下四種:

1)輸入消息:包括鍵盤和鼠標的輸入。這壹類消息首先放在系統消息隊列中,然後由Windows將它們送入應用程序消息隊列中,由應用程序來處理消息。

2)控制消息:用來與Windows的控制對象,如列表框、按鈕、檢查框等進行雙向通信。當用戶在列表框中改動當前選擇或改變了檢查框的狀態時發出此類消息。這類消息壹般不經過應用程序消息隊列,而是直接發送到控制對象上去。

3)系統消息:對程序化的事件或系統時鐘中斷作出反應。壹些系統消息,象DDE消息(動態數據交換消息)要通過Windows的系統消息隊列,而有的則不通過系統消息隊列而直接送入應用程序的消息隊列,如創建窗口消息。

4)用戶消息:這是程序員自己定義並在應用程序中主動發出的,壹般由應用程序的某壹部分內部處理。

4、Windows的消息系統的組成

Windows的消息系統由以下3部分組成:

消息隊列:Windows能夠為所有的應用程序維護壹個消息隊列,應用程序必須從消息隊列中獲去消息,然後分派給某個窗體。

消息循環:通過這個循環機制,應用程序從消息隊列中檢索消息,再把它分派給適當的窗口,然後繼續從消息隊列中檢索下壹條消息,再分派給適當的窗口,依次進行。

窗口過程:每個窗口都有壹個窗口過程,以接收Windows 傳遞給窗口的消息,窗口過程的任務就是獲取消息並且響應它。窗口過程是壹個回調函數,處理完壹個消息後,通常要給Windows 壹個返回值。

5、消息的響應

消息的產生來源於系統事情(包括計時器事件)和用戶事情,Windows用消息來調入和關閉(還有其它處理,如繪制壹個窗口等)應用程序,壹個典型表現是在關機操作中,Windows發壹個關機的消息給所有正在運行的應用程序,告知它們退出內存,此時,應用程序用回應消息的方法來響應OS,因此,消息是應用程序與WinOS交互的手段..

消息產生到被窗口響應的步驟:(如下圖)

1> 系統發生了或用戶發出某個事件。

2> Windows把這個事件翻譯為消息,然後把他放到消息隊列中

3> 應用程序從消息隊列中接受到這個消息,把他存放到TMsg記錄中。

4> 應用程序把消息傳遞給壹個適當的窗體過程。

窗體過程響應這個消息並進行處理。把消息傳遞給了這個窗體的窗體函數。

三、Windows消息機制要點

1. 窗口過程

每個窗口會有壹個稱為窗口過程的回調函數(WndProc),它帶有四個參數,分別為:窗口句柄(Window Handle),消息ID(Message ID),和兩個消息參數(wParam, lParam), 當窗口收到消息時系統就會調用此窗口過程來處理消息。(所以叫回調函數)

2 消息類型

1) 系統定義消息(System-Defined Messages)

在SDK中事先定義好的消息,非用戶定義的,其範圍在[0×0000, 0×03ff]之間, 可以分為以下三類:

窗口消息(Windows Message)

與窗口的內部運作有關,如創建窗口,繪制窗口,銷毀窗口等。可以是壹般的窗口,可以是 Dialog,控件等。

如:WM_CREATE, WM_PAINT, WM_MOUSEMOVE, WM_CTLCOLOR, WM_HSCROLL..

命令消息(Command Message)

與處理用戶請求有關, 如單擊菜單項或工具欄或控件時, 就會產生命令消息。

WM_COMMAND, LOWORD(wParam)表示菜單項,工具欄按鈕或控件的ID。如果是控件, HIWORD(wParam)表示控件消息類型

控件通知(Notify Message)

控件通知消息, 這是最靈活的消息格式, 其Message, wParam, lParam分別為:WM_NOTIFY, 控件ID,指向NMHDR的指針。NMHDR包含控件通知的內容, 可以任意擴展。

2) 程序定義消息(Application-Defined Messages)

用戶自定義的消息, 對於其範圍有如下規定:

WM_USER: 0×0400-0×7FFF (ex. WM_USER+10)

WM_APP(winver>4.0): 0×8000-0xBFFF (ex.WM_APP+4)

RegisterWindowMessage: 0xC000-0xFFFF

3消息隊列(Message Queues)

Windows中有兩種類型的消息隊列

1) 系統消息隊列(System Message Queue)

這是壹個系統唯壹的Queue,設備驅動(mouse, keyboard)會把操作輸入轉化成消息存在系統隊列中,然後系統會把此消息放到目標窗口所在的線程的消息隊列(thread-specific message queue)中等待處理

2) 線程消息隊列(Thread-specific Message Queue)

每壹個GUI線程都會維護這樣壹個線程消息隊列。(這個隊列只有在線程調用GDI函數時才會創建,默認不創建)。然後線程消息隊列中的消息會被送到相應的窗口過程(WndProc)處理.

註意: 線程消息隊列中WM_PAINT,WM_TIMER只有在Queue中沒有其他消息的時候才會被處理,WM_PAINT消息還會被合並以提高效率。其他所有消息以先進先出(FIFO)的方式被處理。

4 隊列消息(Queued Messages)和非隊列消息(Non-Queued Messages)

1)隊列消息(Queued Messages)

消息會先保存在消息隊列中,消息循環會從此隊列中取消息並分發到各窗口處理

如鼠標,鍵盤消息。

2) 非隊列消息(NonQueued Messages)

消息會繞過系統消息隊列和線程消息隊列直接發送到窗口過程被處理

如: WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR, WM_WINDOWPOSCHANGED

註意: postMessage發送的消息是隊列消息,它會把消息Post到消息隊列中; SendMessage發送的消息是非隊列消息, 被直接送到窗口過程處理

5 Windows消息函數

1)PostMessage(PostThreadMessage), SendMessage

PostMessage:把消息放到指定窗口所在的線程消息隊列中後立即返回。 PostThreadMessage:把消息放到指定線程的消息隊列中後立即返回。

SendMessage:直接把消息送到窗口過程處理, 處理完了才返回。

2)GetMessage, PeekMessage

PeekMessage會立即返回 可以保留消息

GetMessage在有消息時返回 會刪除消息

3) TranslateMessage, TranslateAccelerator

TranslateMessage: 把壹個virtual-key消息轉化成字符消息(character message),並放到當前線程的消息隊列中,消息循環下壹次取出處理。

TranslateAccelerator: 將快捷鍵對應到相應的菜單命令。它會把WM_KEYDOWN 或 WM_SYSKEYDOWN轉化成快捷鍵表中相應的WM_COMMAND 或WM_SYSCOMMAND消息, 然後把轉化後的 WM_COMMAND或WM_SYSCOMMAND直接發送到窗口過程處理, 處理完後才會返回。

6消息死鎖( Message Deadlocks

假設有線程A和B, 現在有以下下步驟

1) 線程A SendMessage給線程B, A等待消息在線程B中處理後返回

2)線程B收到了線程A發來的消息,並進行處理, 在處理過程中,B也向線程A SendMessgae,然後等待從A返回。

因為此時, 線程A正等待從線程B返回, 無法處理B發來的消息, 從而導致了\線程A,B相互等待, 形成死鎖。多個線程也可以形成環形死鎖。

可以使用 SendNotifyMessage或SendMessageTimeout來避免出現死鎖。

7 BroadcastSystemMessage

我們壹般所接觸到的消息都是發送給窗口的, 其實, 消息的接收者可以是多種多樣的,它可以是應用程序(applications), 可安裝驅動(installable drivers), 網絡設備(network drivers), 系統級設備驅動(system-level device drivers)等,

BroadcastSystemMessage這個API可以對以上系統組件發送消息。

那麽這些消息是怎樣傳送的呢。我們以MFC為例來看壹下消息傳送過程。

四、MFC消息機制

在Windows應用程序的主函數中,首先要註冊窗口類,然後創建並顯示窗口。創建窗口後程序就進入消息循環,在消息循環中,程序不斷地獲得消息並將消息派送給對應的窗口函數進行處理。

我們可以看到,在MFC的框架結構下,可以進行消息處理的類的頭文件裏面

都會含有DECLARE_MESSAGE_MAP()宏,這裏主要進行消息映射和消息處理函數的聲

明。可以進行消息處理的類的實現文件裏壹般都含有如下的結構。

BEGIN_MESSAGE_MAP(CInheritClass, CBaseClass) //{{AFX_MSG_MAP(CInheritClass)

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

這裏主要進行消息映射的實現和消息處理函數的實現。

所有能夠進行消息處理的類都是基於CCmdTarget類的,也就是說CCmdTarget

類是所有可以進行消息處理類的父類。CCmdTarget類是MFC處理命令消息的基礎和核心。

同時MFC定義了下面的兩個主要結構:

AFX_MSGMAP_ENTRY

struct AFX_MSGMAP_ENTRY

{//“““//

};

和AFX_MSGMAP

struct AFX_MSGMAP

{//“““`//

};

其中AFX_MSGMAP_ENTRY結構包含了壹個消息的所有相關信息, 而AFX_MSGMAP主要作用是兩個,壹:用來得到基類的消息映射入口地址。二:得到本身的消息映射入口地址。

實際上,MFC把所有的消息壹條條填入到AFX_MSGMAP_ENTRY結構中去,形成壹個數組,該數組存放了所有的消息和與它們相關的消息的參數。同時通過AFX_MSGMAP能得到該數組的首地址,同時也得到基類的消息映射入口地址,這是為例當本身對該消息不響應的時候,就調用其基類的消息響應。

現在我們來分析MFC是如何讓窗口過程來處理消息的,實際上所有MFC的窗口類都通過鉤子函數_AfxCbtFilterHook截獲消息,並且在鉤子函數_AfxCbtFilterHook中把窗口過程設定為AfxWndProc。原來的窗口過程保存在成員變量m_pfnSuper中

1.MFC框架下,接收處理來自Windows消息的過程:

2.MFC內部消息處理方式

MFC接收壹個寄送的消息:

MFC處理壹個寄送和發送消息的惟壹明顯不同是寄送的消息要做應用程序的消息隊列中花費壹些時間。在消息泵(message pump)彈出它之前,它要壹直在隊列中。下面是怎樣接受寄送消息的過程。MFC應用程序中的消息泵在CWinApp的成員函數Run()中。應用程序開始運行時,Run()就被調用,Run()把時間分割成兩部分。壹部分用來執行後臺處理,如取消臨時CWnd對象;另壹部分用來檢查消息隊列。當壹個新的消息進來時,Run()抽取它—即用GetMessage( )從隊列中取出該消息,運行PreTranslateMessage( )和::TranslateMessage( )兩個消息翻譯函數,然後用DispatchMessage( )函數調用該消息預期的目標窗口進程。如下圖。

我們用壹個實例函數A發送消息到函數B的過程來看壹下MFC內部消息處理過程。

1. 首先函數A應獲取消息的CWnd類對象的指針,然後,調用CWnd的成員函數SendMessage()。

LRESULT Res=pWnd->SendMessage(UINT Msg, WPARAM wParam, LPARAM lParam);

pWnd指針指向目標CWnd類對象。變量Msg是消息,wParam和lParam變量包含消息的參數,如鼠標單單擊哪裏或選擇了什麽菜單項。目標窗口返回的消息結果放在變量Res中。

發送消息到壹個沒有CWnd的函數對象的窗口,可以用下列目標窗口的句柄直接調用Windows API:

LRESULT Res=::SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

這裏的hWnd是目標窗口的句柄。

如果是異步傳輸也可使用PostMessage(),消息同上相同,但返回值Res不壹樣,Res不是壹個由目標窗體返回的值,而是壹個布爾值,用來表示消息是否成功的放到消息隊列中。

2. 正常情況下,壹旦消息被發送後,應用程序後臺會自動的將它發送,但是在特殊情況下,需要妳自己去刪除壹個消息,例如想在應用程序接收到某種消息之前停止應用程序。有兩種方法可以從應用程序消息隊列中刪除壹個消息,但這兩種方法都沒有涉及MFC。

第壹種方法:在不幹擾任何事情之下窺視消息隊列,看看壹個消息是否在那裏。

BOOL res=::PeekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg ) ;

第二種方法:實際上是等待,壹直等到壹個新的消息到達隊列為止,然後刪除並返回該消息。

BOOL res=::GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);

在這兩種方法中,變量hWnd指定要截獲消息的窗口,如果該變量設為NULL,所有窗口消息將被截獲。wMsgFilterMin和wMsgFilterMax變量與SendMessage( )中的變量Msg相對應,指定查看消息的範圍。如果用“0,0〃,則所有的消息都將被截獲。如果用WM_KEYFIRST,WM_KEYLAST或WM_MOUSEFIRST,WM_MOUSELAST,則所有鍵盤或鼠標的消息將被截獲。wRemoveMsg變量指定PeekMessage( )是否應該真正地從隊列中刪除該消息。(GetMessage( )總是刪除消息)。該變量可以取兩個值:

PM_REMOVE,PeekMessage( )將刪除消息。

PM_NOREMOVE,PeekMessage( )將把消息留在隊列裏,並返回它的壹個拷貝。

當然,如果把消息留在消息隊列中,然後再次調用PeekMessage( )查看相同類型的消息,則將返回完全相同的消息。

lpMsg變量是壹個指向MSG結構的指針,MSG包含檢索到的消息。

typedef struct tagMSG {

HWND hwnd; // window handle message is intended for

UINT message;

WPARAM wParam;

LPARAM lParam;

DWORD time; // the time the message was put in the queue

POINT pt; // the location of the mouse cursor when the

// message was put in the queue

} MSG;

3. 消息會到消息隊列中。CwinApp的成員函數Run,在應用程序運行時,Run就把時間分割成兩部分,壹部分執行後臺的處理,另壹部分來檢查消息的隊列,當發現新消息時,Run就調用GetMessage()從隊列消息中取出該消息。

3.運行兩個消息翻譯函數PreTranslateMessage()和::TranslateMessage(),進行翻譯。主要找到函數對象的位置、消息的動作標識和跟消息相關的執行操作。

4.用DispatchMessage()函數調用該消息預期的函數B進程,進行執行。