Windows 訊息機制

來源:互聯網
上載者:User

Windows作業系統最大的特點就是其圖形化的操作介面,其圖形化介面是建立在其訊息處理機制這個基礎之上的。如果不理解Windows訊息處理機制,肯定無法深入的理解Windows編程。可惜很多程式員對Windows訊息只是略有所聞,對其使用知之甚少,更不瞭解其內部實現原理,本文試著一步一步向大家披露我理解的Windows訊息機制。可以說,掌握了這一部分知識,就是掌握了Windows編程中的神兵利器,靈活運用它,將會極大的提高我們的編程能力。

一、       訊息概述
Windows表單是怎樣展現在螢幕上的呢?眾所周知,是通過API繪製實現的。Windows作業系統提供了一系列的API函數來實現介面的繪製功能,例如:

        DrawText 繪製文字

        DrawEdge 繪製邊框

        DrawIcon 繪製表徵圖

        BitBlt 繪製位元影像

        Rectangle 繪製矩形

        …

再複雜的程式介面都是通過這個函數來實現的。

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

Windows訊息控制中心一般是三層結構,其頂端就是Windows核心。Windows核心維護著一個訊息佇列,第二級控制中心從這個訊息佇列中擷取屬於自己管轄的訊息,後做出處理,有些訊息直接處理掉,有些還要發送給下一級表單(Window)或控制項(Control)。第二級控制中心一般是各Windows應用程式的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)等。這種控制代碼是不能接收訊息的。但不管是哪種控制代碼,都是系統中對象的唯一標識。本文只討論表單控制代碼。

那為什麼控制代碼使視窗具有了如此獨特的特性呢?實際是都是由於訊息的原因。由於有了控制代碼,表單能夠接收訊息,也就知道了該什麼時候繪製自己,繪製子控制項,知道了滑鼠在什麼時候點擊了視窗的哪個部分,從而作出相應的處理。控制代碼就好像是一個人的身份證,有了它,你就可以從事各種社會活動;否則的話,你要麼是一個社會看不到的黑戶,要麼跟在別人後面,通過別人來證明你的存在。

三、            訊息的傳送
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
 

瞭解了這一點後,我們完成可以封裝自己的事件。

通過上面的介紹,相信各位已經對Windows訊息機制有了一定的理解了。通過Windows訊息編程,我們不但可以實現很多常規功能,而且可以實現很多IDE類庫沒有提供的功能;另外,我們還可以通過訊息鉤子,對訊息進行截獲,改變其預設的處理函數,從而突破平台或軟體功能的限制,極大的擴充程式的功能;我們還可以修改預設的表單過程,按自己的要求來響應訊息;或者自訂訊息,實現程式之間的即時通訊等等。通過更加深入的學習,我們還會接觸到更多與Windows訊息機制相關其它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.