windows訊息和訊息佇列執行個體詳解_C 語言

來源:互聯網
上載者:User

本文詳細講述了windows訊息和訊息佇列的原理與應用方法。分享給大家供大家參考。具體分析如下:

與基於MS - DOS的應用程式不同,Windows的應用程式是事件(訊息)驅動的。它們不會顯式地調用函數(如C執行階段程式庫調用)來擷取輸入,而是等待windows向它們傳遞輸入。 windows系統把應用程式的輸入事件傳遞給各個視窗,每個視窗有一個函數,稱為視窗訊息處理函數。視窗訊息處理函數處理各種使用者輸入,處理完成後再將控制權交還給系統。視窗訊息處理函數一般是在註冊一個視窗的時候指定的。你可以從典型的SDK程式中視窗訊息處理函數是怎麼聲明和實現的。

對於Windows XP系統:如果頂層視窗停止回應訊息超過幾秒鐘,系統會認為視窗無回應。在這種情況下,系統將隱藏這個視窗,然後產生一個影子(ghost)視窗覆蓋在它上面。這個影子視窗具有著相同的Z軸順序,位置,大小,顯示內容。影子視窗允許使用者將其移動,調整大小,甚至關閉(關閉的是停止回應的window)。此時只有這幾個動作是被允許的,在偵錯模式下,系統不會產生影子視窗。

本節討論以下主題:

Windows訊息

 1.  訊息類型

 2.  訊息傳遞

 3.  訊息處理

 4.  訊息過濾

 5.  post message和send message

 6.  訊息死結

 7.  廣播訊息

 8.  查詢訊息

現分述如下:

1. Windows訊息

windows通過訊息的形式向視窗傳遞使用者輸入。訊息可以由系統和應用程式產生。該系統會為每個輸入事件產生相應的訊息,

例如,使用者點擊滑鼠,移動滑鼠或捲軸,或是應用程式改變了系統的某些屬性,比如說系統更改了字型資源,改變了某個視窗的

大小。 不僅如此,應用程式可以產生訊息,通告發送訊息指定它的表單去執行某些任務或者是與其他的應用程式互動。

windows系統將訊息發送到一個視窗訊息處理函數時傳遞四個參數:視窗控制代碼,訊息標識符,兩個DWORD值(訊息參數)。

視窗控制代碼標識了該訊息的目的視窗。windows使用它來確定是哪個視窗的的視窗訊息處理函數收到該訊息。

一個訊息標識符是一個有名字的常量,用來表明訊息的意義。當一個視窗處理函數收到一條訊息,它根據判斷訊息標識符來決定如何處理該訊息,例如,訊息標識符WM_PAINT訊息告訴視窗程序視窗的客戶區已發生變化,必須重繪。 訊息參數(DWORD值)指定傳遞的資料或是資料的地址。訊息參數可以是一個整型值,一個指標值。也可以為NULL。

一個視窗過程必鬚根據訊息標識符來確定如何解釋訊息參數。

2. windows 訊息類型

本節描述訊息的兩種類型:

(1) 系統定義的訊息

(2) 應用程式定義的訊息

系統定義的訊息

作業系統嚮應用程式發送訊息來和應用程式通訊。作業系統通過訊息控制應用程式的運行,嚮應用程式傳遞使用者輸入以及一些其他有用的資訊。

應用程式也可以發送系統定義的訊息,應用程式通過這些訊息去控制使用註冊視窗類別建立的控制項的視窗的運行。

每個系統定義的訊息都有一個唯一的訊息標識符和相應的符號常量(在windows SDK的標頭檔裡定義)。符號常量通常會表明系統定義的訊息所屬的類別。不同的首碼表明不同的類別。一下是常見的分類:

    Prefix Message category

    WM        General window(一般的視窗)

    ABM        Application desktop toolbar (應用程式案頭工具條)

    BM        Button control (按鈕控制項)

    CB        Combo box control (下拉式方塊控制項)

    CBEM Extended combo box control(擴充的下拉式方塊控制項)

    CDM        Common dialog box (普通的對話方塊)

    DBT        Device (裝置)

    DL        Drag list box (下拉式清單)

    DM        Default push button control (預設按鈕控制項)

    DTM        Date and time picker control(日期和時間選擇控制項)

    EM        Edit control (編輯控制項)

    HDM        Header control (表頭控制項)

    HKM        Hot key control (熱鍵控制項)

    IPM        IP address control (IP地址控制項)

    LB        List box control  (列表框控制項)

    LVM        List view control (列表視圖控制項)

    MCM        Month calendar control (數學日曆控制項)

    PBM        Progress bar (進度條控制項)

    PGM        Pager control ()

    PSM        Property sheet (屬性頁面)

    RB        Rebar control (分隔條控制項)

    SB        Status bar window (狀態條控制項)

    SBM        Scroll bar control (捲軸控制項)

    STM        Static control (靜態控制項)

    TB        Toolbar (工具條)

    TBM        Trackbar (跟蹤欄)

    TCM        Tab control (索引標籤控制項)

    TTM        Tooltip control ()

    TVM        Tree-view control ()

    UDM        Up-down control ()

應用程式可以通過建立自訂的訊息,用來和自己的視窗和其他進程通訊。如果應用程式建立了自己的訊息,視窗處理函數可以解析這些資訊,並作出相應的處理。

訊息標識符值的取值範圍:

該系統保留了一個訊息範圍,從0x0000到0x03FF(0x03FF等於WM_USER -1)範圍. 這個範圍內的值為系統定義的訊息。應用程式不能使用這些值作為自己的自訂訊息。

從0x0400(數值WM_USER)到0x7FFF的值是為應用程式保留的。應用程式可以使用這個範圍內的值來定義自己的訊息。

如果你的操作系用的版本(windows version)主要版本為4.0版,你還可以使用0x8000(WM_APP)到0xBFF之間的值來定義自己的訊息。

除此之外,應用程式還可以調用RegisterWindowMessage函數註冊一個訊息時,作業系統會返回一個介於0xC000和0xFFFF之間的一個訊息標識符。並且保證這個傳回值是系統唯一的。因此,可以避免和其他應用程式使用的訊息相衝突。

3. 訊息派發

windows使用兩種方法將消派發到一個視窗訊息處理函數:一是將訊息放到訊息佇列(先進先出隊列),二是不放到訊息佇列,直接發送到視窗訊息處理函數,讓視窗處理函數來處理訊息。

派發到訊息佇列的訊息被稱為排隊訊息(Queued messages)。它們主要是使用者輸入事件,比如說滑鼠或鍵盤訊息盤,有WM_MOUSEMOVE訊息,WM_LBUTTONDOWN,WM_KEYDOWN,和WM_CHAR訊息。還有一些其他的,包括WM_TIMER,WM_PAINT,以及WM_QUIT。大多數其他的訊息息,這是直接發送到視窗過程,被稱為非隊列訊息(non queued messages)。

(1) 隊列(Queued)訊息

windows可同時顯示任意數量的視窗。此時,系統使用訊息佇列來將鍵盤和滑鼠事件正確的派發到正確的視窗。

windows維護著一個系統訊息佇列,以及分別為每個GUI線程維護一個各自的線程訊息佇列。為了避免非GUI線程的建立線程訊息佇列的開銷,所有線程建立初始化時,均不建立訊息佇列。只有當線程第一次調用GDI函數時,系統才會為線程建立訊息佇列。所以那些非GUI線程是沒有訊息佇列的。

每當使用者移動滑鼠,點擊按鈕或鍵盤時,滑鼠或鍵盤的裝置驅動程式會將輸入轉換成訊息,並將訊息放在系統訊息佇列裡。刪windows會檢查自己的訊息佇列,如果訊息佇列不為空白,則每次取出並刪除一個訊息,然後確定訊息的目標視窗,然後把訊息放到建立這個視窗的線程的線程訊息佇列裡。線程的訊息佇列接收由線程建立的視窗的所有的滑鼠和鍵盤訊息。然後線程會從隊列中刪除資訊,並告訴系統把它們派發到對應的視窗訊息處理函數。

除了WM_PAINT, WM_TIMER和WM_QUIT訊息以外,系統總是派發放在在訊息佇列的末尾的訊息。這將保證讓一個視窗以first-in, first-out的順序接收訊息。WM_PAINT,WM_TIMER,和WM_QUIT訊息,會一直被儲存在隊列中,只有在隊列中沒有其他訊息時才會被派發到視窗訊息處理函數。此外,同一個視窗的多個WM_PAINT訊息被合并成一個WM_PAINT訊息,客戶區的所有無效部分也會被合并。這樣是為了減少視窗重繪客戶區的次數。

windows向線程訊息佇列傳遞訊息時,首先會填充一個MSG結構,然後將這個MSG結構複製到訊息佇列。MSG中的資訊包括:目標視窗,訊息標識符,兩個訊息參數,訊息派發時的時間,滑鼠游標位置。一個線程可以使用PostMessage或PostThreadMessage功能向自己的訊息佇列或者是其他線程的訊息佇列發送訊息。

應用程式可以使用GetMessage函數從自己的訊息佇列中刪除訊息。查看而不刪除訊息,用的是PeekMessage函數。

PeekMessage函數會返回一個帶有訊息資訊的MSG結構。

從訊息佇列中刪除訊息後,應用程式可以使用DispatchMessage函數指示系統將訊息發送到一個視窗訊息處理函數。 DispatchMessage的參數是是前一次調用GetMessage或PeekMessage獲得的MSG結構的指標。 DispatchMessage會傳遞視窗控制代碼,訊息標識符,這兩個訊息參數這些資訊給視窗訊息處理函數,它不會傳遞訊息派發時間以及滑鼠游標位置。應用程式可以在處理訊息時調用的GetMessageTime和GetMessagePos來獲得這些資訊。

線程可以使用WaitMessage函數,交出自己的控制權,當它的訊息佇列中沒有訊息時,調用WaitMessage函數會掛起線程,直到自己的訊息佇列裡有訊息時才返回。

您可以調用SetMessageExtraInfo函數來關聯一個值給當前線程的訊息佇列。然後調用GetMessageExtraInfo函數來擷取由GetMessage或PeekMessage函數得到的最後一條訊息相關聯的值。你可以去msdn上看更多的關於這幾個函數的資訊。

(2) 非隊列(Nonqueued)訊息

Nonqueued訊息被立即送往目的地的視窗訊息處理函數,繞過了系統的訊息佇列和線程訊息佇列。系統通常會發送nonqueued訊息,來通知那些會影響視窗的事件。例如,當使用者啟用一個新的應用程式視窗時,系統會發送一些列訊息到視窗,包括WM_ACTIVATE,WM_SETFOCUS,WM_SETCURSOR。這些訊息通知視窗被啟用,鍵盤輸入被定向到視窗,並且滑鼠游標也移到視窗的邊界內。

Nonqueued訊息也有可能來源於應用程式調用系統函數。例如,系統調用SetWindowPos函數移動一個視窗後會發送WM_WINDOWPOSCHANGED訊息。 一些函數也發送nonqueued訊息, 有BroadcastSystemMessage,BroadcastSystemMessageEx,SendMessage,SendMessageTimeout,和SendNotifyMessage。 關於這些函數的詳細資料,你可以查閱MSDN。

訊息處理

應用程式必須刪除並處理髮送到它的線程訊息佇列的訊息。單線程應用程式通常在它的WinMain函數的訊息迴圈,刪除和分發訊息到適當的視窗進行處理。多線程應用程式可以在每一個線程建立一個視窗的訊息迴圈。以下章節描述了一個訊息

迴圈如何工作,並講述視窗訊息處理函數的作用:

(1)訊息迴圈

(2)視窗處理函數

訊息迴圈

一個簡單的訊息迴圈包含調用以下三個函數:GetMessage,TranslateMessage,和DispatchMessage。請注意,如果有一個錯誤,GetMessage返回-1 -因此,需要測試它的傳回值,來判斷為-1的情況

程式碼片段:

複製代碼 代碼如下:
...

MSG msg;

BOOL bRet;

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

GetMessage函數從隊列中擷取訊息,並將訊息內容複寫到一個MSG結構。它返回一個非零值,除非遇到WM_QUIT訊息,此種返回FALSE並結束訊息迴圈。在一個單線程應用程式,結束訊息迴圈往往是在關閉應用程式的第一步。應用程式可以調用PostQuitMessage函數來響應WM_DESTROY,結束訊息迴圈。

如果您指定一個視窗控制代碼作為GetMessage的第二個參數,那麼GetMessage只擷取在訊息佇列裡和這個視窗有關的訊息。 GetMessage也可以在隊列中篩選訊息,只擷取指定範圍內的訊息。如需有關訊息過濾的詳細資料,請參考訊息過濾。

線程的訊息迴圈必須包括TranslateMessage,如果線程需要接受鍵盤字元的輸入。每次使用者按下一個鍵,該系統產生相應的虛擬鍵訊息(WM_KEYDOWN和WM_KEYUP)。虛擬鍵訊息包含一個虛擬鍵碼,標識的是被按下的鍵,而不是它相關的字元值。要獲得此值,訊息迴圈必須包含TranslateMessage,用來將虛擬鍵訊息翻譯成字元訊息(WM_CHAR)的並放到應用程式的訊息佇列裡。經過若干次迴圈後,WM_CHAR訊息會被並派發到一個視窗。

DispatchMessage函數將訊息發送到到與MSG結構中的視窗控制代碼關聯的視窗。如果視窗控制代碼是HWND_TOPMOST,DispatchMessage則將訊息發送到作業系統所有的頂層視窗。如果視窗控制代碼是NULL,DispatchMessage不做任何事。

一個應用程式的主線程初始化後,系統就啟動應用程式的訊息迴圈,並創造至少一個視窗。一旦啟動,訊息迴圈持續從該線程的訊息佇列中刪除訊息,並派發他們到相應的視窗。GetMessage函數從訊息列表中擷取到WM_QUIT訊息時,訊息迴圈結束。

一個訊息佇列只需要一個訊息迴圈,即使一個應用程式套件組合含有多個視窗。 DispatchMessage總是調度訊息到正確的視窗,這是因為每個隊列中的訊息是MSG結構,它包含著訊息所屬的視窗的控制代碼。

您可以以多種方式來修改訊息迴圈。例如,您可以從隊列中刪除訊息,但是不派發他們。當發送有些不帶有目的地視窗的訊息時這非常有用。您也可以使用GetMessage只擷取指定的訊息,這是有用的,如果你必須你暫時繞過正常的訊息佇列FIFO順序。

應用程式使用快速鍵時,必須能夠將鍵盤訊息轉換為命令訊息。因此,應用程式的訊息迴圈必須包括TranslateAccelerator函數調用。關於快速鍵的更多資訊,請參見鍵盤加速器。

如果一個線程使用一個無強制回應對話方塊,那麼訊息迴圈必須包括IsDialogMessage函數,以便該對話方塊可以接收鍵盤輸入。

(3)視窗訊息處理函數

視窗訊息函數接收和處理的所有發送到視窗的訊息。每個視窗類別有一個視窗訊息處理函數,用該類建立的每個視窗使用同一視窗訊息處理函數。

該系統將訊息發送到一個視窗的程式,並傳遞訊息的相關資訊到視窗訊息處理函數,視窗訊息處理函數檢查訊息標識符,根據傳過來的參數識別並處理不同的訊息,

一個視窗過程通常不會忽略一個訊息。如果訊息沒有被處理,必須被發送給系統預設的視窗訊息處理函數,這是否通過調用DefWindowProc函數,來執行一個預設的處理,並返回一個處理的結果。視窗程序必須然後返回該值作為自己的訊息處理的結果。大多數視窗訊息處理函數只處理一少部分訊息,並將其他的返回給系統預設的視窗訊息處理函數。

因為視窗訊息處理函數被所有屬於同一個視窗類別的視窗共用,它可以處理幾個不同的視窗的訊息。要確定具體的視窗訊息,視窗訊息處理函數可以檢查訊息結構裡的視窗控制代碼。

希望本文所述對大家的Windows應用程式設計有所協助。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.