古詩詞大全網 - 成語解釋 - windows消息處理機制的消息實現

windows消息處理機制的消息實現

Windows窗體是怎樣展現在屏幕上的呢?眾所周知,是通過API繪制實現的。Windows操作系統提供了壹系列的API函數來實現界面的繪制功能,例如:

DrawText 繪制文字

DrawEdge 繪制邊框

DrawIcon 繪制圖標

Bitmap 繪制位圖

Rectangle 繪制矩形

再復雜的程序界面都是通過這些函數來實現的。

那什麽時候調用這些函數呢?顯然我們需要壹個控制中心,用來進行“發號施令”,我們還需要壹個命令傳達機制,將命令即時的傳達到目的地。這個控制中心,就是壹個動力源,就像壹顆心臟,源源不斷地將血液送往各處。這個命令傳達機制就是Windows消息機制,Windows消息就好比是身體中的血液,它是命令傳達的使者。

Windows消息控制中心壹般是三層結構,其頂端就是Windows內核。Windows內核維護著壹個消息隊列,第二級控制中心從這個消息隊列中獲取屬於自己管轄的消息,後做出處理,有些消息直接處理掉,有些還要發送給下壹級窗體(Window)或控件(Control)。第二級控制中心壹般是各Windows應用程序的Application對象。第三級控制中心就是Windows窗體對象,每壹個窗體都有壹個默認的窗體過程,這個過程負責處理各種接收到的消息。如下圖所示:

(註: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、自定義消息

編程人員還可以自定義消息。 不是每個控件都能接收消息,轉發消息和繪制自身,只有具有句柄(handle)的控件才能做到。有句柄的控件本質上都是壹個窗體(window),它們可以獨立存在,可以作為其它控件的容器,而沒有句柄的控件,如Label,是不能獨立存在的,只能作為窗口控件的子控件,它不能繪制自身,只能依靠父窗體將它繪制來。

句柄的本質是壹個系統自動維護的32位的數值,在整個操作系統的任壹時刻,這個數值是唯壹的。但該句柄代表的窗體釋放後,句柄也會被釋放,這個數值又可能被其它窗體使用。也就是說,句柄的數值是動態的,它本身只是壹個唯壹性標識,操作系統通過句柄來識別和查找它所代表的對象。

然而,並非所有的句柄都是窗體的句柄,Windows系統中還中很多其它類型的句柄,如畫布(hdc)句柄,畫筆句柄,畫刷句柄,應用程序句柄(hInstance)等。這種句柄是不能接收消息的。但不管是哪種句柄,都是系統中對象的唯壹標識。本文只討論窗體句柄。

那為什麽句柄使窗口具有了如此獨特的特性呢?實際是都是由於消息的原因。由於有了句柄,窗體能夠接收消息,也就知道了該什麽時候繪制自己,繪制子控件,知道了鼠標在什麽時候點擊了窗口的哪個部分,從而作出相應的處理。句柄就好像是壹個人的身份證,有了它,妳就可以從事各種社會活動;否則的話,妳要麽是壹個社會看不到的黑戶,要麽跟在別人後面,通過別人來證明妳的存在。 1、從消息隊列獲取消息:

可以通過PeekMessage或GetMessage函數從Windows消息隊列中獲取消息。Windows保存的消息隊列是以線程(Thread)來分組的,也就是說每個線程都有自己的消息隊列。

2、發送消息

發送消息到指定窗體壹般通過以下兩個函數完成:SendMessage和PostMessage。兩個函數的區別在於:PostMessage函數只是向線程消息隊列中添加消息,如果添加成功,則返回True,否則返回False,消息是否被處理,或處理的結果,就不知道了。而SendMessage則有些不同,它並不是把消息加入到隊列裏,而是直接翻譯消息和調用消息處理(線程向自己發送消息才是這樣),直到消息處理完成後才返回。所以,如果我們希望發送的消息立即被執行,就應該調用SendMessage。

還有壹點,就是SendMessage發送的消息由於不會被加入到消息隊列中(錯:線程向其他線程發送消息也是追加到其他線程的發送消息隊列的,即使這兩個線程在同壹個進程也是如此),所以通過PeekMessage或GetMessage是不能獲取到由SendMessage發送的消息。

另外,有些消息用PostMessage不會成功,比如wm_settext。所以不是所有的消息都能夠用PostMessage的。

還有壹些其它的發送消息API函數,如PostThreadMessage,SendMessageCallback,SendMessageTimeout,SendNotifyMessage等。 消息循環是應用程序能夠持續存在的根本原因。如果循環退出,則應用程序就結束了。

我們來看壹看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操作系統調用了,在壹個窗口創建的時候,在分配窗體句柄的時候就需要傳入回調函數地址。那為什麽我們平時編程看不到這個回調函數呢?這是由於我們的編程工具已經為我們生成了默認的窗體過程,這個過程的要做的事情就是判斷不同的消息類型,然後做出不同的處理。例如可以為鍵盤或鼠標輸入生成事件等。 事件本質上是對消息的封裝,是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