程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 深入理解Delphi的消息機制

深入理解Delphi的消息機制

編輯:Delphi
  一、窗口的創建

  VCL 中,具有句柄(Handle) 屬性的真正窗口控件全部繼承自 TWinControl,那就從 TWinControl 的創建開始說起。

  VCL 中窗口的建立不是按照我們想象中的流程創建的,即先把所有的窗口都創建好,然後再調用,而是在需要時才創建。可能你還不能理解我這句話的意思,慢慢看。繼承自 TWinControl 的窗口控件都會有 Handle 屬性,當代碼中需要 Handle 值時,通過該屬性的 getter 調用 TWinControl.HandleNeeded 來獲得句柄,這時如果窗體已經建立,直接返回句柄,否則先創建窗口實例,再返回句柄,因此窗口創建是在 TWinControl.HandleNeeded 中實現的。Borland 這樣做的目的我想是最大程度地來節省系統資源吧。

  TWinControl.HandleNeeded 中有幾個重要的方法,通過他們才得以創建窗口。TWinControl.HandleNeeded 調用TWinControl.CreateHandle 來獲得 Handle。但 CreateHandle 只是個包裝函數,它首先調用 TWinControl.CreateWnd 來創建窗口,CreateWnd 是一個重要的過程,它先調用 TWinControl.CreateParams 設置創建窗口的參數,通過這些參數調用 RegisterClass API 注冊窗口類,CreateWnd 然後調用 TWinControl.CreateWindowHandle,CreateWindowHandle 才是真正調用 CreateWindowEx API 創建窗口實例的函數。CreateHandle、CreateWnd、CreateParams、CreateWindowHandle都是虛方法,派生類可以重載這些方法以獲得更多的功能 ,其中 CreateParams 被重載的幾率最大。

  上面提到的方法源碼我建議你都要仔細看一遍,加深印象,後面我提到的方法,你也都要看看源碼,受益無窮呀,我將不再提示。

  至此一個窗口算是建立起來了,但是還是無法正確運行,因為它還沒有消息循環。

  二,消息循環的實現

  消息循環的實現是整個 VCL 消息框架中寫得最精彩的地方,因為傳統的 Windows 回調函數是一個靜態函數,而 VCL 中的窗體是類,調用類方法時,除了函數本身的地址,還需一個 Self,在它們之間建立關聯真不是一件容易的事情,需要大量的代碼技巧,同時消息循環還要保證每秒鐘能處理幾百到幾萬次的消息量,因此代碼更需要寫得精巧。 研習這部分代碼可能會花比較多的時間。

  我們知道注冊窗體類時就要提供窗體回調函數入口地址,那麼可以想象到 VCL 中這個過程是發生在對 TWinControl.CreateWnd 的調用中,在該方法中,靜態函數指針 @InitWndProc 被賦值給 WNDCLASSEX 結構中的 lpfnWndProc,這是 VCl 窗體首次建立消息循環的地方。
InitWndProc 第一次被調用時,通過 SetWindowLong API 將消息回調函數替換成 TWindowControl.FObjectInstance,而TWinControl.FObjectInstance 就是一個普通的 Pointer,賦值是在 TWinControl.Create 中通過那個最具 Magic 的函數 MakeObjectInstance 完成的,這個過程非常復雜,詳細描述見參考[3]。

  替換的結果是類方法 TWinControl.MainWndProc 成為真正的消息處理 Handler,隨後的對應窗體實例的消息處理全部在 TWinControl.MainWndProc 中完成。其中還有一個細節就是消息在被 MainWndProc 處理之前還要調用一個純匯編寫的靜態函數 -- StdWndProc 將消息統一派發[1]。至此完成消息回調從普通的靜態函數到類方法的轉變。

  事實上 TWinControl.MainWndProc 是調用 WindowsProc 來實際處理窗口消息,在 TControl.Create 中 WindowsProc 是被指定成類中虛擬方法 WndProc。從 TControl 到實際的 VCL 窗體類這條繼承鏈上,很多派生類都重載了 WndProc,從而每個重載該方法的派生類都會增加一些功能。當然在繼承鏈的末端,例如 TForm,也可以重載 WndProc,來完成一些 tricky 代碼。記住,如果你重載 WndProc,總是先處理自己想要的消息,然後將不處理的消息遞交到父類的 WndProc 中處理。

  在每一個繼承類的 WndProc 中應該只處理維持窗體運作的最基本的消息,其他不處理的消息最終會在 TControl.WndProc 中被傳遞到 TObject.Diapatch。TObject.Diapatch 在自己和父類的動態方法表中查詢相應消息 ID,如果找到了,則調用相應的方法。所有處理消息的類方法都應該以關鍵字 message 定義,這可以保證其入口地址都是存在動態方法表中,從而也保證需要處理的消息 可以在 TObject.Diapatch 執行過程中被調用。

  如果在動態方表中還是無法查詢到需要處理的消息,那麼 TObject.Diapatch 會繼續調用虛方法 DefaultHandler,TObject.DefaultHandler 只是個 PlaceHolder,該方法在 TWinControl 中被重載, TWinControl 繼承類中鮮有繼續重載該方法的類,可以認為消息最後一次被處理的機會就是發
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved