淺談windows消息機制


首先來了解幾個基本概念:

消息:在了解什么是消息先來了解什么是事件。事件可分為幾種,由輸入設備觸發的,比如鼠標鍵盤等等。由窗體控件觸發的,比如button控件,file菜單等。還有就是來自Windows內部的事件。這三種稱為事件。而消息,是由事件翻譯而來的。事件產生消息。

從數據結構角度來講,消息是一種結構體。結構如下:

 1 typedef struct tagMSG
 2 
 3 {
 4 
 5     HWND hwnd;  //窗口句柄。
 6 
 7     UINT message;//消息類型。
 8 
 9     WPARAM wParam;//32位附加信息。
10 
11     LPARAM lParam;//32位附加信息。
12 
13     DWORD time;//消息發送時間。
14 
15     POINT pt;//消息發送時,鼠標所在位置。
16 
17 }MSG;

消息隊列:消息隊列有兩種,分為系統消息隊列和應用程序消息隊列。產生的消息首先由Windows系統捕獲,放在系統消息隊列,再拷貝到對應的應用程序消息隊列。32/64位系統為每一個應用程序維護一個消息隊列。

消息循環:系統為每個應用程序維護一個消息循環,消息循環會不斷檢索自身的消息隊列。每有一個消息,就用GetMessage()取出消息。

1 while(GetMessage (&msg, NULL, 0, 0))//Windows消息循環。
2 
3 {
4     TranslateMessage (&msg) ;//翻譯消息,如按鍵消息,翻譯為WM_CHAR
5 
6     DispatchMessage (&msg) ;//分發消息到對應窗口
7 
8 }

GetMessage具有阻塞機制。當消息隊列中沒有消息時,程序非忙等,而是讓權等待。當收到WM_QUIT時,GetMessage返回false,循環停止,同時應用程序終止。

消息處理:DispatchMessage()把取出來的消息分配給相應的窗口或線程,由窗口過程處理函數DefWindowProc()處理。

==============================================================================================================================================================================割割割割割

以上簡要地介紹了消息隊列,消息循環與消息處理的概念。

Windows的應用程序靠消息驅動來實現功能。而消息驅動靠消息機制來處理。消息機制就是由消息隊列,消息循環,消息處理構成的。

那么,消息機制是如何運作的呢?

當用戶運行一個應用程序,通過對鼠標的點擊或鍵盤按鍵,產生一些特定事件。由於Windows一直監控着I/O設備,該事件首先會被翻譯成消息,由系統捕獲,存放於系統消息隊列。經分析,Windows知道該消息應由那個應用程序處理,則拷貝到相應的應用程序消息隊列。由於消息循環不斷檢索自身的消息隊列,當發現應用程序消息隊列里有消息,就用GetMessage()取出消息,封裝成Msg()結構。如果該消息是由鍵盤按鍵產生的,用TranslateMessage()翻譯為WM_CHAR消息,否則,用DisPatchMessage()將取出的消息分發到相應的應用程序窗口,交由窗口處理程序處理。Windows為每個窗體預留了過程窗口函數,該函數是一個回掉函數,由系統調用,應用程序不能調用。程序員可以通過重載該函數處理我們”感興趣”的消息。對於不感興趣的消息,則由系統默認的窗口過程處理程序做出處理。

下面看這么一張圖:

 

這張圖很好地解釋了消息機制的運行原理。 

當運行程序->事件操作引發消息->消息先存在系統消息隊列->再存入到應用程序消息隊列->用消息循環提取消息->處理消息->再返回消息隊列....

 

==============================================================================================================================================================以上是系統消息的產生與處理

 

同樣,我們可以通過自定義用戶消息,用SendMessage()函數向窗口模擬發送消息,再重載窗口過程處理函數DefWndProc()來接收用戶自定義消息,並作出處理。

因為SendMessage()是系統的一個API函數。因此在.NET平台中調用API函數需要,涉及到動態鏈接庫的概念。需用動態鏈接庫聲明一個類,該類表明,用到哪個.DLL文件,以及程序入口點。

例如:

1 int xxx1 = 0x520;
2 int xxx2 = 0x521; //用戶自定義消息。
1 [DllImport("User32.dll",EntryPoint="SendMessage")]//EntryPoint 表示要調用的函數入口點是dll文件里的SendMessage()函數。
2 private static extern int SendMessage(
3   ntPtr hwnd,     //聲明一個窗口句柄,所謂句柄,就是C#里的"指針",窗口或其他資源的一個引用,但此"指針"不可用於其他特殊操作。                              4                            //IntPtr是平台特定的int類型。就是在32跟64位系統上分別為32位和64位。一般用於聲明一個句柄。
4   int Msg,    //表明要發送的消息。
5   int wParam,  //32位的附加信息。
6   int IPrarm  //32位的附加信息。(隨消息的改變而改變)
7  );
8 // 此處由於SendMessage發送到本線程的窗口。所以發送的消息不會被加入到消息隊列中,所以通過PeekMessage()或GetMessage()不能獲取到由SendMessage發送的消息。
1 protected override void DefWndProc(ref Message m)//重載接收處理函數
2 {
3     switch (m.Msg)
4   {
5     case xxx1: label1.Text = "在嗎"; break;
6     case xxx2: label1.Text = "沒空"; break; //XXX1 /XXX2 用戶自定義的消息。
7     default: base.DefWndProc(ref m); break;
8   }
9 }

什么是動態鏈接庫?姑且看為”倉庫”,里邊放着一大堆待調用的函數。動態則表明隨用隨取,而不是程序用不用得上都要帶上一大堆代碼。C# 中用[DllImport]類聲明調用API函數時,有三個條件,一是,函數名必須與原API函數名一致,二是參數也得一致,三是必須聲明為”extern”。關於API函數的參數在MSDN上可以查到。這里不再贅述。

學過C/C++的朋友知道,寫程序都要加個#include<xxx.h>的頭文件,其實這就是動態鏈接。

====================================================================================================================================以上便是用戶自定義消息,以及發送消息,和接收處理消息

下面由一個窗體的創建再講一遍。Windows應用程序實際上具有相同的程序結構和執行控制流程。執行流程如下:

程序入口點(DOS下是main() ,windows下是_tWinMain())->注冊窗口類(RegisterClass)->創建窗口(CreateWindow())->顯示窗口(ShowWindow(hwnd,nCmdShow)->(UpdateWindow(hwnd))->消息循環(等待用戶操作窗口產生消息)->放入消息隊列->消息循環往復讀取並執行相應代碼->窗口函數(決定在窗口那里顯示些什么,或者如何響應用戶輸入(如上面的SendMessage()函數))->消息處理(用來確定窗口函數接收的是什么消息以及如何處理(如上面的DefWndProc()函數)。

題外話:Windows和Dos編程,一個很大的區別就是,Windows編程是事件驅動,而Dos是過程驅動。所以,要學好Windows編程,起碼要對消息機制有一個清楚的認識,否則很難編出多有用的程序來。

轉載請注明出處:

博客園 :http://www.cnblogs.com/gu-zhan/   老咸出品


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM