複製鏈接
請複製以下鏈接發送給好友

windows消息處理機制

鎖定
消息,就是指Windows發出的一個通知,告訴應用程序某個事件發生了。例如,單擊鼠標、改變窗口尺寸、按下鍵盤上的一個鍵都會使Windows發送一個消息給應用程序。
中文名
windows消息處理機制
組    成
消息隊列應用程序,·消息循環
類    型
計算機領域術語

windows消息處理機制簡介

消息本身是作為一個記錄傳遞給應用程序的,這個記錄(一般在 C/C++/彙編 中稱為“結構體”)中包含了消息的類型以及其他信息。例如,對單擊鼠標所產生的消息來説,這個記錄(結構體)中包含了單擊鼠標的消息號(WM_LBUTTONDOWN)、單擊鼠標時的座標(由X,Y值連接而成的一個32位整數)。這個記錄類型叫做TMsg。
在Delphi的Windows單元中是這樣聲明的:
type
TMsg = packed record
hwnd: HWND; / /窗口句柄
message: UINT; / /消息常量標識符
wParam: WPARAM ; // 32位消息的特定附加信息(以前是16位)
lParam: LPARAM ; // 32位消息的特定附加信息
time: DWORD; / /消息創建時的時間
pt: TPoint; / /消息創建時的鼠標位置
end ;
其中TPoint定義是:
TPoint= record
x:integer;
y:integer;
end;
在C語言中的定義是:
typedef struct Msg
{HWND hwnd; / /窗口句柄UINT message; / /消息常量標識符WPARAM wParam; // 32位消息的特定附加信息LPARAM lParam; // 32位消息的特定附加信息DWORD time; / /消息創建時的時間TPoint pt; / /消息創建時的鼠標位置}TMsg;typedef struct TPoint}
{int x;int y;}TPoint;

windows消息處理機制消息內容

hwnd
32位的窗口句柄。窗口可以是任何類型的屏幕對象,因為Win32能夠維護大多數可視對象的句柄(窗口、對話框、按鈕、編輯框等)。
message
用於區別其他消息的常量值,這些常量可以是Windows單元中預定義的常量,也可以是自定義的常量。
wParam
通常是一個與消息有關的常量值,也可能是窗口或控件的句柄。
lParam
通常是一個指向內存中數據的指針
由於WParam、lParam和Pointer都是32位的,即等同於DWORD。因此,它們之間可以相互轉換。

windows消息處理機制消息系統

Windows的消息系統是由3個部分組成的:
·消息隊列。Windows能夠為所有的應用程序維護一個消息隊列。應用程序必須從消息隊列中獲取
消息,然後分派給某個窗口。
·消息循環。通過這個循環機制應用程序從消息隊列中檢索消息,再把它分派給適當的窗口,然
後繼續從消息隊列中檢索下一條消息,再分派給適當的窗口,依次進行。
· 窗口過程。每個窗口都有一個窗口過程來接收傳遞給窗口的消息,它的任務就是獲取消息然後
響應它。窗口過程是一個回調函數;處理了一個消息後,它通常要返回一個值給Windows。
注意回調函數是程序中的一種函數,它是由Windows或外部模塊調用的。
一個消息從產生到被一個窗口響應,其中有5個步驟:
1) 系統中發生了某個事件。
2) Windows把這個事件翻譯為消息,然後把它放到消息隊列中。
3)應用程序從消息隊列中接收到這個消息,把它存放在TMsg記錄中。
4)應用程序把消息傳遞給一個適當的窗口的窗口過程。
5) 窗口過程響應這個消息並進行處理。
步驟3和4構成了應用程序的消息循環。消息循環往往是Windows應用程序的核心,因為消息循環使一個應用程序能夠響應外部的事件。消息循環的任務就是從消息隊列中檢索消息,然後把消息傳遞給適當的窗口。如果消息隊列中沒有消息,Windows就允許其他應用程序處理它們的消息。
Windows操作系統最大的特點就是其圖形化的操作界面,其圖形化界面是建立在其消息處理機制這個基礎之上的。如果不理解Windows消息處理機制,肯定無法深入的理解Windows編程。可惜很多程序員對Windows消息只是略有所聞,對其使用知之甚少,更不瞭解其內部實現原理,本文試着一步一步向大家披露我理解的Windows消息機制。可以説,掌握了這一部分知識,就是掌握了Windows編程中的神兵利器,靈活運用它,將會極大的提高我們的編程能力。

windows消息處理機制消息實現

windows消息處理機制消息概述

Windows窗體是怎樣展現在屏幕上的呢?眾所周知,是通過API繪製實現的。Windows操作系統提供了一系列的API函數來實現界面的繪製功能,例如:
DrawText 繪製文字
DrawEdge 繪製邊框
DrawIcon 繪製圖標
Bitmap 繪製位圖
Rectangle 繪製矩形
再複雜的程序界面都是通過這些函數來實現的。
那什麼時候調用這些函數呢?顯然我們需要一個控制中心,用來進行“發號施令”,我們還需要一個命令傳達機制,將命令即時的傳達到目的地。這個控制中心,就是一個動力源,就像一顆心臟,源源不斷地將血液送往各處。這個命令傳達機制就是Windows消息機制,Windows消息就好比是身體中的血液,它是命令傳達的使者。
Windows消息控制中心一般是三層結構,其頂端就是Windows內核。Windows內核維護着一個消息隊列,第二級控制中心從這個消息隊列中獲取屬於自己管轄的消息,後做出處理,有些消息直接處理掉,有些還要發送給下一級窗體(Window)或控件(Control)。第二級控制中心一般是各Windows應用程序的Application對象。第三級控制中心就是Windows窗體對象,每一個窗體都有一個默認的窗體過程,這個過程負責處理各種接收到的消息。如圖1所示:
圖1 圖1
(注:windows指windows操作系統;窗口:即windows窗口;窗體:包括窗口,以及有句柄的控件;control指控件,控件本身也可能是一個window,也可能不是;Application即應用程序,應用程序也可能不會用到Windows消息機制,這裏我們專門討論有消息循環的應用程序)
消息是以固定的結構傳送給應用程序的,結構如下:
Public Type MSG
hwnd As Long
message As Long
wParam As Long
lParam As Long
time As Long
pt As POINTAPI
End Type
其中hwnd是窗體的句柄,message是一個消息常量,用來表示消息的類型,wParam和lParam都是32位的附加信息,具體表示什麼內容,要視消息的類型而定,time是消息發送的時間,pt是消息發送時鼠標所在的位置。
Windows操作系統中包括以下幾種消息:
1、標準Windows消息:
這種消息以WM_打頭。
2、通知消息
通知消息是針對標準Windows控件的消息。這些控件包括:按鈕(Button)、組合框(ComboBox)、編輯框(TextBox)、列表框(ListBox)、ListView控件、Treeview控件、工具條(Toolbar)、菜單(Menu)等。每種消息以不同的字符串打頭。
3、自定義消息
編程人員還可以自定義消息。

windows消息處理機制句柄

不是每個控件都能接收消息,轉發消息和繪製自身,只有具有句柄(handle)的控件才能做到。有句柄的控件本質上都是一個窗體(window),它們可以獨立存在,可以作為其它控件的容器,而沒有句柄的控件,如Label,是不能獨立存在的,只能作為窗口控件的子控件,它不能繪製自身,只能依靠父窗體將它繪製來。
句柄的本質是一個系統自動維護的32位的數值,在整個操作系統的任一時刻,這個數值是唯一的。但該句柄代表的窗體釋放後,句柄也會被釋放,這個數值又可能被其它窗體使用。也就是説,句柄的數值是動態的,它本身只是一個唯一性標識,操作系統通過句柄來識別和查找它所代表的對象。
然而,並非所有的句柄都是窗體的句柄,Windows系統中還中很多其它類型的句柄,如畫布(hdc)句柄,畫筆句柄,畫刷句柄,應用程序句柄(hInstance)等。這種句柄是不能接收消息的。但不管是哪種句柄,都是系統中對象的唯一標識。本文只討論窗體句柄。
那為什麼句柄使窗口具有了如此獨特的特性呢?實際是都是由於消息的原因。由於有了句柄,窗體能夠接收消息,也就知道了該什麼時候繪製自己,繪製子控件,知道了鼠標在什麼時候點擊了窗口的哪個部分,從而作出相應的處理。句柄就好像是一個人的身份證,有了它,你就可以從事各種社會活動;否則的話,你要麼是一個社會看不到的黑户,要麼跟在別人後面,通過別人來證明你的存在。

windows消息處理機制消息傳送

1、從消息隊列獲取消息:
可以通過PeekMessage或GetMessage函數從Windows消息隊列中獲取消息。Windows保存的消息隊列是以線程(Thread)來分組的,也就是説每個線程都有自己的消息隊列。
2、發送消息
發送消息到指定窗體一般通過以下兩個函數完成:SendMessage和PostMessage。兩個函數的區別在於:PostMessage函數只是向線程消息隊列中添加消息,如果添加成功,則返回True,否則返回False,消息是否被處理,或處理的結果,就不知道了。而SendMessage則有些不同,它並不是把消息加入到隊列裏,而是直接翻譯消息和調用消息處理(線程向自己發送消息才是這樣),直到消息處理完成後才返回。所以,如果我們希望發送的消息立即被執行,就應該調用SendMessage。
還有一點,就是SendMessage發送的消息由於不會被加入到消息隊列中(錯:線程向其他線程發送消息也是追加到其他線程的發送消息隊列的,即使這兩個線程在同一個進程也是如此),所以通過PeekMessage或GetMessage是不能獲取到由SendMessage發送的消息。
另外,有些消息用PostMessage不會成功,比如wm_settext。所以不是所有的消息都能夠用PostMessage的。
還有一些其它的發送消息API函數,如PostThreadMessage,SendMessageCallbackSendMessageTimeoutSendNotifyMessage等。

windows消息處理機制消息循環

消息循環是應用程序能夠持續存在的根本原因。如果循環退出,則應用程序就結束了。
我們來看一看Delphi中封裝的消息循環是怎樣的:
第一步:程序開始運行(Run)
Application.Initialize; //初始化
Application.CreateForm(TForm1, Form1); //創建主窗體
Application.Run; //開始運行,準備進行消息循環
如果不創建主窗體,應用程序同樣可以存在和運行。
第二步:開始調用消息循環(HandleMessage)
procedure TApplication.Run;
begin
FRunning := True;
try
AddExitProc(DoneApplication);
if FMainForm <> nil then
begin
case CmdShow of
SW_SHOWMINNOACTIVE: FMainForm.FWindowState := wsMinimized;
SW_SHOWMAXIMIZED: MainForm.WindowState := wsMaximized;
end;
if FShowMainForm then
if FMainForm.FWindowState = wsMinimized then
Minimize else
FMainForm.Visible := True;
Repeat  //注:循環開始
try
HandleMessage;
except
HandleException(Self);
end;
until Terminated; //循環結束條件
end;
finally
FRunning := False;
end;
end;
第三步:消息循環中對消息的處理。
procedure TApplication.HandleMessage;
var
Msg: TMsg;
begin
if not ProcessMessage(Msg) then Idle(Msg);
end;
function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
var
Handled: Boolean;
begin
Result := False;
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
begin
Result := True;
if Msg.Message <> WM_QUIT then
begin
Handled := False;
if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
if not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) and
not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end
else
FTerminate := True;
end;
end;
窗體過程實際上是一個回調函數。所謂的回調函數,實際上就是由Windows操作系統或外部程序調用的函數。回調函數一般都有規定的參數格式,以地址方式傳遞給調用者。窗口過程中是Windows操作系統調用了,在一個窗口創建的時候,在分配窗體句柄的時候就需要傳入回調函數地址。那為什麼我們平時編程看不到這個回調函數呢?這是由於我們的編程工具已經為我們生成了默認的窗體過程,這個過程的要做的事情就是判斷不同的消息類型,然後做出不同的處理。例如可以為鍵盤或鼠標輸入生成事件等。

windows消息處理機制消息事件

事件本質上是對消息的封裝,是IDE編程環境為了簡化編程而提供的有用的工具。這個封裝是在窗體過程中實現的。每種IDE封裝了許多Windows的消息,例如:
事件
消息
OnActivate
WM_ACTIVATE
OnClick
WM_XBUTTONDOWN
OnCreate
WM_CREATE
OnDblClick
WM_XBUTTONDBLCLICK
OnKeyDown
WM_KEYDOWN
OnKeyPress
WM_CHAR
OnKeyUp
WIN_KEYUP
OnPaint
WM_PAINT
OnResize
WM_SIZE
OnTimer
WM_TIMER