訊息與訊息佇列

來源:互聯網
上載者:User

訊息與訊息佇列 

 
About Messages and Message Queues

 

基於windows的應用程式是事件驅動的。它們不進行明確的函數調用(比如調用C運行庫)來獲得輸入。取而代之的是他們等待作業系統將輸入傳遞給他們。

作業系統傳遞所有的輸入給各種應用程式。每個window有一個函數叫做視窗過程,只要有輸入作業系統就調用他們。視窗過程處理輸入後,將控制返回給作業系統。詳細資料請看視窗過程。

 

Microsoft? Windows? XP:如果最頂層(top-level)的視窗停止回應幾秒鐘,系統將認為視窗被掛起。這種情況下,系統將隱藏這個視窗並用一個ghost window在相同z座標上,相同的大小,相同的可視屬性來取代他。以允許使用者移動視窗,改變視窗大小,關閉視窗。然而,僅有這些行為是可操作的因為應用程式已掛起。在Debugger模式下,系統不產生ghost視窗。

 

This section discusses the following topics:

? Windows Messages

? Message Types

? Message Routing

? Message Handling

? Message Filtering

? Posting and Sending Messages

? Message Deadlocks

? Broadcasting Messages

? Query Messages

 

Windows Messages

系統以messages的形式將輸入傳遞給視窗過程。訊息可以被系統和應用程式產生。系統產生訊息當每一次輸入事件發生,如當使用者敲鍵盤,移滑鼠,或者單擊捲軸。系統也用訊息來響應由於應用程式引起的系統變化。如應用程式改變系統字型資源或者改變它的一個視窗。應用程式產生訊息來讓他的視窗執行一些任務或者與其他應用程式的視窗通訊。

 

系統發送訊息給視窗過程並傳遞4個參數,windows控制代碼, 訊息標識符, 2個訊息參數。系統使用表單控制代碼來決定那個視窗過程來接受訊息。

 

訊息標識符以常量命名指出訊息的含義。當視窗過程接收到訊息,使用訊息標識符決定如何處理訊息。例如、WM_PAINT告訴視窗過程表單客戶區被改變了需要重繪。

 

訊息參數指定被視窗過程使用的資料和資料的位置。其含義和值取決於訊息類型。訊息參數可以包含一個整數, 標誌位,一個指標等。 當訊息不使用訊息參數時,他們被設定位NULL。一個Window視窗過程必鬚根據訊息標識符來決定如何解釋訊息參數。

 

Message Types

This section describes the two types of messages:

? System-Defined Messages

? Application-Defined Messages

System-Defined Messages

當系統和應用程式通訊時,系統post和send系統定義訊息。他使用訊息來控制應用程式的操作,提供輸入和其他資訊讓應用程式處理。應用程式也可以post或者send系統定義訊息。

 

每一個系統定義訊息由一個唯一的標識符與一致的常量以聲明訊息的含義。例如 WM_PAINT 要求視窗繪製它的內容。

 

符號常量指定系統定義訊息屬於的類別,常量的首碼指定處理解釋訊息的表單的類型。以下使一些首碼和他們相關的訊息類別。

 

Prefix Message category

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

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

WM General window

 

通用表單訊息覆蓋了很大一個資訊和請求的範圍, 包括滑鼠鍵盤訊息, 菜單對話方塊的輸入, 表單產生與管理, 動態資料交換 (DDE).

 

Application-Defined Messages

 

應用程式可以產生自己用的訊息或者與其他進程中表單通訊。如果應用程式產生自己的訊息,視窗過程接受並且必須提供合適的處理。

 

系統保留訊息標識符的值在0x0000在0x03ff(WM_USER-1)範圍。這些值被系統定義訊息使用。 應用程式不能使用這些值給自己的訊息。

 

private window classes用0x0400(WM_USER)到0x7fff訊息標識符

 

? If your application is marked version 4.0, you can use message-identifier values in the range 0x8000 (WM_APP) through 0xBFFF for private messages.

 

系統使用RegisterWindowMessage來註冊訊息,返回一個訊息標識符範圍在0XC000到0XFFFF,使用這個函數來保證整個系統範圍內是唯一的

 

Message Routing

系統有兩個方法將訊息傳遞到視窗過程。Post一個訊息到先進先出的訊息佇列。系統定義的臨時記憶體對象。和直接 send訊息到視窗過程。

被發送到訊息佇列的訊息稱做入隊訊息,主要是由滑鼠鍵盤輸入,例如 WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_KEYDOWN, and WM_CHAR messages。還包括定時器, 重新整理, 退出: WM_TIMER, WM_PAINT, and WM_QUIT。其他直接發送到視窗過程的訊息被稱為 非入隊訊息。

 

Queued Messages

 

系統可以顯示任何數量的表單同一時間。為了傳遞滑鼠鍵盤訊息到合適的視窗,系統使用訊息佇列。

 

系統維護一個系統訊息佇列和每一個GUI線程的訊息佇列,為避免給non-GUI現成建立訊息佇列,所有線程產生時並沒有訊息佇列。僅當線程第一次調用GDI函數時,系統給線程建立一個訊息佇列。只要使用者移動滑鼠,點擊滑鼠,敲鍵,驅動程式將其轉換為訊息將他們放在系統訊息佇列中。系統將他們從系統訊息佇列中移走,檢查他們的目標視窗,然後將他們發送到建立目標視窗的線程的訊息佇列。線程訊息佇列接收由這個線程建立的所有視窗的滑鼠鍵盤訊息。線程刪除訊息系統調用視窗過程進行處理。

WM_PAINT是一個例外,系統總是將訊息Post在訊息佇列的末尾。這樣保證視窗以先進先出的順序接受訊息。然而, 僅當沒有其他訊息時WM_PAINT才被傳遞視窗過程。同一個視窗的多個 WM_PAINT被合并成一個 WM_PAINT 訊息, 合并所有的無效地區到一個無效地區。合并WM_PAIN減少了重新整理視窗的次數。

 

系統通過填充MSG結構並將它複製到訊息佇列來發送訊息到線程隊列。MSG結構包括:視窗控制代碼,訊息標識符,兩個訊息參數。 訊息被posted的時間, 和滑鼠的位置。線程可以使用PostMessage和PostThreadMessage來給發送訊息到自己訊息佇列或者另一個線成的訊息佇列。

 

應用程式可以使用GetMessage從訊息佇列刪除訊息。可以使用 PeekMessage來檢查一個訊息而不刪除它。 這個函數將訊息佇列的訊息填充到MSG結構。

 

在從訊息佇列刪除了一個訊息,應用程式可以使用DispatchMessage使系統將訊息發送到視窗過程來處理。DispatchMessage擁有一個指向由GetMessage或者PeekMessage填充的MSG結構的指標,傳遞視窗控制代碼,訊息標識符,訊息參數給視窗過程。但它並不傳遞訊息發送的時間和滑鼠的位置,應用程式可以通過GetMessageTime和GetMessagePos來得到這些資訊。

線程可以使用WaitMessage將控制交給其他線程當訊息佇列中沒有他的訊息隊烈時,這個函數掛起線程,並不返回,直到新的訊息放置於訊息佇列中。

 

你可以調用SetMessageExtraInfo函數來關聯一個值到當前線程的訊息佇列。調用GetMessageExtraInfo來得到這個與最後一次通過GetMessage或者PeekMessaage獲得的訊息關聯得值。

 

Nonqueued Messages

 

非入隊訊息即直接發送到視窗過程的訊息,繞過系統隊列和線程訊息佇列。系統發送非入隊訊息通知一個視窗事件,例如,當使用者啟用一個新的應用程式視窗,系統發送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。這些訊息通知視窗它被啟用了,鍵盤輸入被直接傳遞到視窗,滑鼠在當前視窗內移動。 非入隊訊息也可以由當應用程式調用系統函數產生。例如,當程式調用SetWindowPos系統發送WM_WINDOWPOSCHANGED訊息。

 

一些函數也發送非入隊訊息如 BroadcastSystemMessage, BroadcastSystemMessageEx, SendMessage, SendMessageTimeout, and SendNotifyMessage.

 

Message Handling

 

應用程式必須移除和處理被post到訊息佇列的訊息。單線程應用程式通常在WinMain使用訊息迴圈來移除和分發訊息到何時視窗過程來處理。多線程應用程式可以在每一個建立視窗的線程中中包含訊息迴圈。

 

Message Loop

A simple message loop consists of one function call to each of these three functions: GetMessage, TranslateMessage, and DispatchMessage. Note that if there is an error, GetMessage returns -1 -- thus the need for the special testing.

Show Example

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結構。他返回一個非0值,除非遇到WM_QUIT訊息。否則它返回0然後結束迴圈。In a single-threaded application, ending the message loop is often the first step in closing the application. 應用程式可以使用PostQuitMessage來結束自己的訊息迴圈。通常在主視窗的WM_DESTROY訊息中調用。

 

如果你將一個視窗控制代碼作為第二個參數傳入GetMessage,那麼只有指定視窗的的訊息可以從隊列中獲得。GetMessage也可以從訊息佇列中過濾訊息只接受訊息佇列中落在範圍內的訊息。詳細見訊息過濾。 一個線程迴圈必須包括TranslateMessage如果線程接受鍵盤的字元輸入。每一次使用者按鍵系統產生虛擬鍵訊息,一個虛擬鍵訊息包含虛擬鍵用來標誌那個鍵被按下,並不是他的字元值,要獲得這個值訊息迴圈必須調用TranslateMessage,用於將虛擬鍵轉換為字元訊息WM_CHAR然後將它放回應用程式訊息佇列。通過將它轉寄到視窗過程,字元訊息被刪除。

 

DispatchMessage 函數分發訊息到MSG結構中的視窗控制代碼關聯的視窗過程。如果視窗控制代碼是HWND_TOPMOST,DispatchMessage分發訊息到系統中的所有的top-level視窗的視窗過程。如果控制代碼是NULL,DispatchMessage不做任何事。

應用程式得主線程在初始化,建立至少一個視窗後啟動它的訊息迴圈,一旦啟動訊息迴圈持續從線程隊列中獲得訊息,然後分發他們到合適的視窗。訊息迴圈在通過GetMessage得到WM_QUIT並將他從隊列中刪除後結束。

 

一個訊息都列僅需要一個訊息迴圈, 即使程式包含很多視窗。DispatchMessage 總是分發訊息到合適的視窗,這是因為MSG結構包含訊息所屬的視窗的控制代碼。

 

你可以用各種方式修改訊息迴圈。例如,從訊息佇列獲得訊息訊息但不把他們分發到視窗中去。當應用程式發送一個訊息但不指定視窗時是有用的。你可以用GetMessage獲得一個特定的訊息,而保留其他訊息在訊息佇列。當你需要改變先進先出的順序時是有用的。

 

應用程式使用加速鍵必須將鍵盤訊息轉換為WM_COMMAND訊息。所以訊息迴圈必須包括TranslateAccelerator函數。詳細資料參見加速鍵。

 

如果線程使用非模態對話方塊,訊息迴圈必須包括 IsDialogMessage 以使非模態對話方塊獲得鍵盤輸入。

 

Window Procedure

 

視窗過程是一個用於處理所有發送到這個視窗的訊息的函數。任何一個視窗類別都有一個視窗過程。同一個類的視窗使用同樣的視窗過程來響應訊息。

 

系統發送訊息給視窗過程將訊息資料作為參數傳遞給他,視窗過程使用參數產生合適行為。

 

一個視窗過程通常不忽略訊息,如果他不處理,它會將訊息傳回到執行預設的處理。視窗過程通過調用DefWindowProc來做這個處理。視窗過程必須return一個值作為它的訊息處理結果。大多數視窗只處理小部分訊息和將其他的通過DefWindowProc傳遞給系統做預設的處理。

 

因為視窗過程是同一個類的視窗共用的,它可以為不同的視窗處理訊息。通過檢查訊息中的視窗控制代碼來找到被訊息影響的視窗。詳細資料請看Window Procedures。

 

Message Filtering

 

應用程式可以從訊息佇列選擇特定的訊息。使用GetMessage或者PeekMessage並指定一個訊息過濾器。這個過濾器是一個訊息標識符的範圍或者是一個表單控制代碼,或者兩者同時指定。GetMessage和PeekMessage使用過濾器來選擇從訊息佇列中獲得那些訊息。當應用程式要尋找一個後入訊息佇列的訊息是很有用。

 

一個應用程式過濾訊息時必須保證符合過濾條件的訊息能被發送。例如應用程式在視窗中過濾WM_CHAR並不能得到鍵盤輸入,GetMessage並不返回。這將掛起應用程式。

 

Posting and Sending Messages

 

應用程式可以post和send訊息,通過將訊息複製到訊息佇列即post訊息,send訊息將訊息資料作為參數直接傳遞到視窗過程。

可以使用PostMessage來post訊息,SendMessage,BroadcastSystemMessage, SendMessageCallback, SendMessageTimeout, SendNotifyMessage, or SendDlgItemMessage來send訊息。

 

Posting Messages

 

應用程式post訊息通知指定表單執行任務。PostMessage可以建立MSG結構並將它Copy到訊息佇列。訊息迴圈最終捕獲訊息並分發到合適的視窗過程。

 

給PostMessage傳遞一個NULL控制代碼不指定哪一個視窗,這個訊息就被發送到當前線程訊息佇列,應用程式必須在訊息處理中處理這個訊息。這是為整個應用程式發送訊息的一個方法。

 

偶爾你可以使用HWND_TOPMOST 這個參數作為控制代碼參數向所有的top-level視窗發送訊息。

 

當訊息佇列滿的時候PostMessage並不發送訊息,應用程式需要檢查PostMessage函數的傳回值來確定訊息是否被發送,或者沒有需要重發。

 

Sending Messages

 

通過Send訊息來通知視窗過程立即執行任務。SendMessage將訊息發送給指定視窗的視窗過程。函數將等待視窗過程處理完才返回一個訊息結果。父視窗和子視窗通常使用Send訊息來互相通訊。例如,一個父視窗擁有以一個文字框作為它的子視窗,它可以通過發送訊息到子視窗來給文字框設定文字。子視窗也可將文字被使用者改變的訊息發送給父視窗。

 

SendMessageCallback也將訊息發送給指定視窗的視窗過程,但是他立即返回。在視窗過程處理完訊息後,系統調用指定的回呼函數,回呼函數的詳細資料參見SendAsyncProc

 

偶爾,你可以發送訊息系統中到所有的top-level視窗,例如,應用程式改變了系統時間。它必須以HWND_TOPMOST作為控制代碼參數發送一個WM_TIMECHANGE 訊息通知所有的top-level視窗,你也可以將lpdwRecipients 指定為BSM_APPLICATIONS 用BroadcastSystemMessage函數向所有應用程式廣播。

 

可以使用InSendMessage或者InSendMessageEx函數,視窗過程可以判斷它處理的訊息是否是由其他線程調用SendMessage發送過來的。This capability is useful when message processing depends on the origin of the message.

 

Message Deadlocks

 

一個線程可以調用SendMessage想其他線程發送訊息,這個線程不能繼續執行直到獲得訊息得視窗過程返回。如果接受訊息的線程處理訊息時yields控制,發送線程的訊息將永遠得不到執行,因為他在等待SendMessage返回。如果接受線程和發送線成語同一個訊息佇列聯絡起來,它可能導致訊息死結。

Note that the receiving thread need not yield control explicitly; calling any of the following functions can cause a thread to yield control implicitly.

接受線程不需要明確yield控制,下面任何一個函數回導致一個線程明確yield控制。

 

? DialogBox

? DialogBoxIndirect

? DialogBoxIndirectParam

? DialogBoxParam

? GetMessage

? MessageBox

? PeekMessage

? SendMessage

為了避免潛在的死結,可以使用SendNotifyMessage 或者 SendMessageTimeout,否則視窗過程,將用InSendMessage或者InSendMessageEx判斷訊息是否由另一個線程發送過來。當調用前面任何一個函數視窗過程將首先調用InSendMessage或者InSendMessagEx,如果函數返回true視窗過程必須在引起線程yeild控制前調用ReplyMessage 。

 

Broadcasting Messages

 

每一個訊息包括訊息標識符和兩個參數,wParam和lParam,訊息標識符是唯一的代表這個訊息的含義。參數提供與訊息相關的額外的資訊,但是wParam參數通常是一個類型值提供更多的訊息資訊。

 

訊息廣播是簡單的將訊息發送到系統中的多個接收者。使用BroadcastSystemMessage函數來廣播訊息,你必須指定一個或者多個接收者類型,這些類型可以是applications, installable drivers, network drivers, and system-level device drivers。系統將訊息發送給指定類型的所有成員。

 

系統廣播訊息來響應系統裝置驅動程式或者組件的變化。驅動程式或相關組件廣播訊息給應用程式和其他組件以通知他們這些變化。例如,負責磁碟響應的組件廣播訊息只要磁碟片驅動程式發現媒體的變化,如當使用者將磁碟插入磁碟機。

 

系統按一下順序廣播訊息給接收者:系統級的裝置驅動程式,網路驅動程式,installable drivers,和應用程式。意味著如果系統級裝置驅動程式作為接收者總是第一個有機會來響應訊息。在接受者類型中,沒有一個驅動程式能保證在其他驅動程式前接受一個訊息。即一個給特定驅動程式的訊息必須有一個全域唯一的標誌使其他不關心這個訊息的驅動程式不處理它。

 

你也可以廣播訊息給所有頂層視窗通過在SendMessage等函數中指定HWND_BROADCAST。

應用程式通過頂層視窗的視窗過程來接收訊息。訊息不發送到子視窗。服務可以接收訊息通過視窗過程或者他們的服務控制函數。

 

Query Messages

 

你可以建立自訂的訊息使用它們來調整你的應用程式和其他組件之間的行為。這個非常有用如果你已建立你自己的installable drivers和系統機裝置驅動程式。你的驅動程式和使用這個驅動程式的應用程式可以通過自訂訊息互相傳遞資訊。

 

To poll recipients for permission to carry out a given action, use a query message.你可以通過在dwFlags參數中設定BSF_QUERY調用BroadcastSystemMessage。每一個query message的接收者必須返回TRUE來將訊息發送到下一個接收者。如果任何一個接收者返回BROADCAST_QUERY_DENY,廣播立即停止函數返回0。

 

Windows 95/98/Me: 你可以建立廣播和處理訊息的installable drivers。一個installable drivers是一個匯出DriverProc函數的dll。驅動程式通過它的DriverProc來接收訊息。Installable drivers典型的用來支援多媒體裝置,例如sound boards,也可以用於其他裝置和目的。

 

Windows 95/98/Me: 網路驅動程式是給應用程式提供以下支援的dlls。系統級裝置驅動程式是系統特定的提供直接存取和管理電腦硬體的可動作項目。這些組件如何處理系統訊息超出了這篇文章的範圍。
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.