深度解析VC中的訊息傳遞機制(上)

ForTechnology發表於2011-08-07
深度解析VC中的訊息傳遞機制(上)
2006-03-24 00:04:06  www.hackbase.com  來源:網際網路
摘要:Windows 摘要:Windows程式設計和Dos程式設計,一個很大的區別就是,Windows程式設計是事件驅動,訊息傳遞的。所以,要學好Windows程式設計,必須對訊息機制有一個清楚的認識,本文希望能夠對訊息的傳遞做一個全面的分析。
   什麼是訊息?

   訊息系統對於一個win32程式來說十分重要,它是一個程式執行的動力源泉。一個訊息,是系統定義的一個32位的值,他唯一的定義了一個事件,向 Windows發出一個通知,告訴應用程式某個事情發生了。例如,單擊滑鼠、改變視窗尺寸、按下鍵盤上的一個鍵都會使Windows傳送一個訊息給應用程式。

   訊息本身是作為一個記錄傳遞給應用程式的,這個記錄中包含了訊息的型別以及其他資訊。例如,對於單擊滑鼠所產生的訊息來說,這個記錄中包含了單擊滑鼠時的座標。這個記錄型別叫做MSG,MSG含有來自windows應用程式訊息佇列的訊息資訊,它在Windows中宣告如下:

typedef struct tagMsg
{
HWND hwnd; 接受該訊息的視窗控制程式碼
UINT message; 訊息常量識別符號,也就是我們通常所說的訊息號
WPARAM wParam; 32位訊息的特定附加資訊,確切含義依賴於訊息值
LPARAM lParam; 32位訊息的特定附加資訊,確切含義依賴於訊息值
DWORD time; 訊息建立時的時間
POINT pt; 訊息建立時的滑鼠/游標在螢幕座標系中的位置
}MSG;

   訊息可以由系統或者應用程式產生。系統在發生輸入事件時產生訊息。舉個例子, 當使用者敲鍵, 移動滑鼠或者單擊控制元件。系統也產生訊息以響應由應用程式帶來的變化, 比如應用程式改變系統字型改變窗體大小。應用程式可以產生訊息使窗體執行任務,或者與其他應用程式中的視窗通訊。

   訊息中有什麼?

   我們給出了上面的註釋,是不是會對訊息結構有了一個比較清楚的認識?如果還沒有,那麼我們再試著給出下面的解釋:

   hwnd 32位的視窗控制程式碼。視窗可以是任何型別的螢幕物件,因為Win32能夠維護大多數可視物件的控制程式碼(視窗、對話方塊、按鈕、編輯框等)。

   message用於區別其他訊息的常量值,這些常量可以是Windows單元中預定義的常量,也可以是自定義的常量。訊息識別符號以常量命名的方式指出訊息的含義。當視窗過程接收到訊息之後,他就會使用訊息識別符號來決定如何處理訊息。例如、WM_PAINT告訴視窗過程窗體客戶區被改變了需要重繪。符號常量指定系統訊息屬於的類別,其字首指明瞭處理解釋訊息的窗體的型別。

   wParam 通常是一個與訊息有關的常量值,也可能是視窗或控制元件的控制程式碼。

   lParam 通常是一個指向記憶體中資料的指標。由於WParam、lParam和Pointer都是32位的,因此,它們之間可以相互轉換。

訊息識別符號的值

   系統保留訊息識別符號的值在0x0000在0x03ff(WM_USER-1)範圍。這些值被系統定義訊息使用。 應用程式不能使用這些值給自己的訊息。應用程式訊息從WM_USER(0X0400)到0X7FFF,或0XC000到0XFFFF;WM_USER到 0X7FFF範圍的訊息由應用程式自己使用;0XC000到0XFFFF範圍的訊息用來和其他應用程式通訊,我們順便說一下具有標誌性的訊息值:

WM_NULL---0x0000 空訊息。
0x0001----0x0087 主要是視窗訊息。
0x00A0----0x00A9 非客戶區訊息
0x0100----0x0108 鍵盤訊息
0x0111----0x0126 選單訊息
0x0132----0x0138 顏色控制訊息
0x0200----0x020A 滑鼠訊息
0x0211----0x0213 選單迴圈訊息
0x0220----0x0230 多文件訊息
0x03E0----0x03E8 DDE訊息
0x0400 WM_USER
0x8000 WM_APP
0x0400----0x7FFF 應用程式自定義私有訊息

   訊息有的分類?

   其實,windows中的訊息雖然很多,但是種類並不繁雜,大體上有3種:視窗訊息、命令訊息和控制元件通知訊息。

   視窗訊息大概是系統中最為常見的訊息,它是指由作業系統和控制其他視窗的視窗所使用的訊息。例如CreateWindow、DestroyWindow和MoveWindow等都會激發視窗訊息,還有我們在上面談到的單擊滑鼠所產生的訊息也是一種視窗訊息。

   命令訊息,這是一種特殊的視窗訊息,他用來處理從一個視窗傳送到另一個視窗的使用者請求,例如按下一個按鈕,他就會向主視窗傳送一個命令訊息。

   控制元件通知訊息,是指這樣一種訊息,一個視窗內的子控制元件發生了一些事情,需要通知父視窗。通知訊息只適用於標準的視窗控制元件如按鈕、列表框、組合框、編輯框,以及Windows公共控制元件如樹狀檢視、列表檢視等。例如,單擊或雙擊一個控制元件、在控制元件中選擇部分文字、操作控制元件的滾動條都會產生通知訊息。 她類似於命令訊息,當使用者與控制元件視窗互動時,那麼控制元件通知訊息就會從控制元件視窗傳送到它的主視窗。但是這種訊息的存在並不是為了處理使用者命令,而是為了讓主視窗能夠改變控制元件,例如載入、顯示資料。例如按下一個按鈕,他向父視窗傳送的訊息也可以看作是一個控制元件通知訊息;單擊滑鼠所產生的訊息可以由主視窗直接處理,然後交給控制元件視窗處理。

   其中視窗訊息及控制元件通知訊息主要由視窗類即直接或間接由CWND類派生類處理。相對視窗訊息及控制元件通知訊息而言,命令訊息的處理物件範圍就廣得多,它不僅可以由視窗類處理,還可以由文件類,文件模板類及應用類所處理。

   由於控制元件通知訊息很重要的,人們用的也比較多,但是具體的含義往往令初學者暈頭轉向,所以我決定把常見的幾個列出來供大家參考:

   按扭控制元件

BN_CLICKED 使用者單擊了按鈕
BN_DISABLE 按鈕被禁止
BN_DOUBLECLICKED 使用者雙擊了按鈕
BN_HILITE 用/戶加亮了按鈕
BN_PAINT 按鈕應當重畫
BN_UNHILITE 加亮應當去掉

   組合框控制元件

CBN_CLOSEUP 組合框的列表框被關閉
CBN_DBLCLK 使用者雙擊了一個字串
CBN_DROPDOWN 組合框的列表框被拉出
CBN_EDITCHANGE 使用者修改了編輯框中的文字
CBN_EDITUPDATE 編輯框內的文字即將更新
CBN_ERRSPACE 組合框記憶體不足
CBN_KILLFOCUS 組合框失去輸入焦點
CBN_SELCHANGE 在組合框中選擇了一項
CBN_SELENDCANCEL 使用者的選擇應當被取消
CBN_SELENDOK 使用者的選擇是合法的
CBN_SETFOCUS 組合框獲得輸入焦點

   編輯框控制元件

EN_CHANGE 編輯框中的文字己更新
EN_ERRSPACE 編輯框記憶體不足
EN_HSCROLL 使用者點選了水平滾動條
EN_KILLFOCUS 編輯框正在失去輸入焦點
EN_MAXTEXT 插入的內容被截斷
EN_SETFOCUS 編輯框獲得輸入焦點
EN_UPDATE 編輯框中的文字將要更新
EN_VSCROLL 使用者點選了垂直滾動條訊息含義

   列表框控制元件

LBN_DBLCLK 使用者雙擊了一項
LBN_ERRSPACE 列表框記憶體不夠
LBN_KILLFOCUS 列表框正在失去輸入焦點
LBN_SELCANCEL 選擇被取消
LBN_SELCHANGE 選擇了另一項
LBN_SETFOCUS 列表框獲得輸入焦點

佇列訊息和非佇列訊息

   從訊息的傳送途徑來看,訊息可以分成2種:佇列訊息和非佇列訊息。訊息佇列由可以分成系統訊息佇列和執行緒訊息佇列。系統訊息佇列由Windows維護,執行緒訊息佇列則由每個GUI執行緒自己進行維護,為避免給non-GUI現成建立訊息佇列,所有執行緒產生時並沒有訊息佇列,僅當執行緒第一次呼叫GDI函式數系統給執行緒建立一個訊息佇列。佇列訊息送到系統訊息佇列,然後到執行緒訊息佇列;非佇列訊息直接送給目的視窗過程。

   對於佇列訊息,最常見的是滑鼠和鍵盤觸發的訊息,例如WM_MOUSERMOVE,WM_CHAR等訊息,還有一些其它的訊息,例如:WM_PAINT、 WM_TIMER和WM_QUIT。當滑鼠、鍵盤事件被觸發後,相應的滑鼠或鍵盤驅動程式就會把這些事件轉換成相應的訊息,然後輸送到系統訊息佇列,由 Windows系統去進行處理。Windows系統則在適當的時機,從系統訊息佇列中取出一個訊息,根據前面我們所說的MSG訊息結構確定訊息是要被送往那個視窗,然後把取出的訊息送往建立視窗的執行緒的相應佇列,下面的事情就該由執行緒訊息佇列操心了,Windows開始忙自己的事情去了。執行緒看到自己的訊息佇列中有訊息,就從佇列中取出來,通過作業系統傳送到合適的視窗過程去處理。

   一般來講,系統總是將訊息Post在訊息佇列的末尾。這樣保證視窗以先進先出的順序接受訊息。然而,WM_PAINT是一個例外,同一個視窗的多個 WM_PAINT被合併成一個 WM_PAINT 訊息, 合併所有的無效區域到一個無效區域。合併WM_PAIN的目的是為了減少重新整理視窗的次數。

   非佇列訊息將會繞過系統佇列和訊息佇列,直接將訊息傳送到視窗過程,。系統傳送非佇列訊息通知視窗,系統傳送訊息通知視窗。 例如,當使用者啟用一個視窗系統傳送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。這些訊息通知視窗它被啟用了。非佇列訊息也可以由當應用程式呼叫系統函式產生。例如,當程式呼叫SetWindowPos系統傳送WM_WINDOWPOSCHANGED訊息。一些函式也傳送非佇列訊息,例如下面我們要談到的函式。

   訊息的傳送

   瞭解了上面的這些基礎理論之後,我們就可以進行一下簡單的訊息傳送與接收。

   把一個訊息傳送到視窗有3種方式:傳送、寄送和廣播。

   傳送訊息的函式有SendMessage、SendMessageCallback、SendNotifyMessage、 SendMessageTimeout;寄送訊息的函式主要有PostMessage、PostThreadMessage、 PostQuitMessage;廣播訊息的函式我知道的只有BroadcastSystemMessage、 BroadcastSystemMessageEx。

   SendMessage的原型如下:LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam),這個函式主要是向一個或多個視窗傳送一條訊息,一直等到訊息被處理之後才會返回。不過需要注意的是,如果接收訊息的視窗是同一個應用程式的一部分,那麼這個視窗的視窗函式就被作為一個子程式馬上被呼叫;如果接收訊息的視窗是被另外的執行緒所建立的,那麼視窗系統就切換到相應的執行緒並且呼叫相應的視窗函式,這條訊息不會被放進目標應用程式佇列中。函式的返回值是由接收訊息的視窗的視窗函式返回,返回的值取決於被髮送的訊息。

   PostMessage的原型如下:BOOL PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam),該函式把一條訊息放置到建立hWnd視窗的執行緒的訊息佇列中,該函式不等訊息被處理就馬上將控制返回。需要注意的是,如果hWnd引數為HWND_BROADCAST,那麼,訊息將被寄送給系統中的所有的重疊視窗和彈出視窗,但是子視窗不會收到該訊息;如果hWnd引數為NULL,則該函式類似於將dwThreadID引數設定成當前執行緒的標誌來呼叫PostThreadMEssage函式。

   從上面的這2個具有代表性的函式,我們可以看出訊息的傳送方式和寄送方式的區別所在:被髮送的訊息是否會被立即處理,函式是否立即返回。被髮送的訊息會被立即處理,處理完畢後函式才會返回;被寄送的訊息不會被立即處理,他被放到一個先進先出的佇列中,一直等到應用程式空線的時候才會被處理,不過函式放置訊息後立即返回。

   實際上,傳送訊息到一個視窗處理過程和直接呼叫視窗處理過程之間並沒有太大的區別,他們直接的唯一區別就在於你可以要求作業系統截獲所有被髮送的訊息,但是不能夠截獲對視窗處理過程的直接呼叫。

   以寄送方式傳送的訊息通常是與使用者輸入事件相對應的,因為這些事件不是十分緊迫,可以進行緩慢的緩衝處理,例如滑鼠、鍵盤訊息會被寄送,而按鈕等訊息則會被髮送。

   廣播訊息用得比較少,BroadcastSystemMessage函式原型如下:

long BroadcastSystemMessage(DWORD dwFlags,LPDWORD lpdwRecipients,UINT uiMessage,WPARAM wParam,LPARAM lParam);

   該函式可以向指定的接收者傳送一條訊息,這些接收者可以是應用程式、可安裝的驅動程式、網路驅動程式、系統級別的裝置驅動訊息和他們的任意組合。需要注意的是,如果dwFlags引數是BSF_QUERY並且至少一個接收者返回了 BROADCAST_QUERY_DENY,則返回值為0,如果沒有指定BSF_QUERY,則函式將訊息傳送給所有接收者,並且忽略其返回值。 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25897606/viewspace-704358/,如需轉載,請註明出處,否則將追究法律責任。

相關文章