狠狠爱成人网_日韩一级在线_国产综合自拍_亚洲精品韩国_亚洲视频导航_麻豆成人在线播放_欧美jjzz_一区在线视频观看_美脚丝袜一区二区三区在线观看_欧美91视频

當(dāng)前位置:系統(tǒng)之家 > 技術(shù)開發(fā)教程 > 詳細頁面

Delphi的消息機制

Delphi的消息機制

更新時間:2019-09-24 文章作者:未知 信息來源:網(wǎng)絡(luò) 閱讀次數(shù):

  永遠記住,無論你是用 SDK 還是借用 VCL 來創(chuàng)建窗口,都要遵循 Windows 的游戲規(guī)則,即先注冊窗口類,然后再創(chuàng)建窗口實例,在消息循環(huán)中寫實現(xiàn)代碼。你還要知道 Windows 已經(jīng)為了我們預(yù)注冊了多個窗口類,例如“Edit”、“ComboBox”,這時候我們要做的就是直接創(chuàng)建這些窗口,無需注冊窗口類了;在 Delphi 中這一切更簡單了,VCL 全部為你做好了,你只需簡單地在設(shè)計窗體上拖動你要的控件再寫實現(xiàn)代碼就可以了,是不是很 cool?
  一、窗口的創(chuàng)建

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

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

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

  上面提到的方法源碼我建議你都要仔縛匆槐椋由鈑∠螅竺嫖姨岬降姆椒ǎ鬩捕家純叢綽耄芤嫖耷鈦劍醫(yī)輝傯崾盡?BR>
  至此一個窗口算是建立起來了,但是還是無法正確運行,因為它還沒有消息循環(huán)。

  二,消息循環(huán)的實現(xiàn)

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

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

  替換的結(jié)果是類方法 TWinControl.MainWndProc 成為真正的消息處理 Handler,隨后的對應(yīng)窗體實例的消息處理全部在 TWinControl.MainWndProc 中完成。其中還有一個細節(jié)就是消息在被 MainWndProc 處理之前還要調(diào)用一個純匯編寫的靜態(tài)函數(shù) -- StdWndProc 將消息統(tǒng)一派發(fā)[1]。至此完成消息回調(diào)從普通的靜態(tài)函數(shù)到類方法的轉(zhuǎn)變。

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

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

  如果在動態(tài)方表中還是無法查詢到需要處理的消息,那么 TObject.Diapatch 會繼續(xù)調(diào)用虛方法 DefaultHandler,TObject.DefaultHandler 只是個 PlaceHolder,該方法在 TWinControl 中被重載, TWinControl 繼承類中鮮有繼續(xù)重載該方法的類,可以認為消息最后一次被處理的機會就是發(fā)生在 TWinControl.DefaultHandler 中。我們知道在消息循環(huán)中不處理的消息最后都應(yīng)該交給 Windows 的默認回調(diào)函數(shù) DefWindowProc API 來處理, TWinControl.DefaultHandler 最主要的工作就是完成這個,除此之外,還完成幾個額外的消息處理[2]。

  VCL 的消息流程至此為止。

  可能你還在為整個消息分派流程犯暈,讓我用實例來分析一下吧。

  三、VCL 完整的消息分派流程

  1. TButton

  新建一個 Application,在 Form1 上放一個 Button (缺省名為Button1),在其 OnClick 事件中隨便寫點代碼,加上斷點,在調(diào)試之前,請打開 DCU 調(diào)試開關(guān)(Project->Options->Compiler->Use Debug DCUs), 這個開關(guān)如果不打開,是沒法調(diào)試 VCL 的,然后 F9 運行,當(dāng)停留在斷點上時,打開Call Stack 窗口(View->Debug Window->Call Stack)可看到調(diào)用順序如下(從底往上看):

TForm1.Button1Click($9637C0)
TControl.Click
TButton.Click
TButton.CNCommand((48401, 660, 0, 524948, 0))
TControl.WndProc((48401, 660, 524948, 0, 660, 0, 660, 8, 0, 0))
TWinControl.WndProc((48401, 660, 524948, 0, 660, 0, 660, 8, 0, 0))
TButtonControl.WndProc((48401, 660, 524948, 0, 660, 0, 660, 8, 0, 0))
TControl.Perform(48401,660,524948)
DoControlMsg(524948,(no value))
TWinControl.WMCommand((273, 660, 0, 524948, 0))
TCustomForm.WMCommand((273, 660, 0, 524948, 0))
TControl.WndProc((273, 660, 524948, 0, 660, 0, 660, 8, 0, 0))
TWinControl.WndProc((273, 660, 524948, 0, 660, 0, 660, 8, 0, 0))
TCustomForm.WndProc((273, 660, 524948, 0, 660, 0, 660, 8, 0, 0))
TWinControl.MainWndProc((273, 660, 524948, 0, 660, 0, 660, 8, 0, 0))
StdWndProc(918056,273,660,524948)
TWinControl.DefaultHandler((no value))
TControl.WMLButtonUp((514, 0, 48, 13, (48, 13), 0))
TControl.WndProc((514, 0, 852016, 0, 0, 0, 48, 13, 0, 0))
TWinControl.WndProc((514, 0, 852016, 0, 0, 0, 48, 13, 0, 0))
TButtonControl.WndProc((514, 0, 852016, 0, 0, 0, 48, 13, 0, 0))
TWinControl.MainWndProc((514, 0, 852016, 0, 0, 0, 48, 13, 0, 0))
StdWndProc(524948,514,0,852016)
TApplication.HandleMessage
TApplication.Run
Project1

  一個 Button 被點擊,在 TButton 內(nèi)部會發(fā)生兩個消息:WM_LBUTTONDOWN/WM_LBUTTONUP, TButton 沒有處理 WM_LBUTTONUP(問題:為什么只響應(yīng) WM_LBUTTONUP,這兩個消息只應(yīng)該發(fā)生在 Windows 原生控件內(nèi),除非 TButton subclass 了 "Button",這部分代碼我沒看),只是交給 TWinControl.DefaultHandler,隨后 TButton 又將生成的 WM_COMMAND 消息發(fā)送給它的 Parent,即 TForm,經(jīng)過一系列消息傳遞, WM_COMMAND 在 TWinControl.WMCommand 中被處理,通過 DoControlMsg 將 WM_COMMAND 加工成 CN_COMMAND,再利用 TControl.Perform 將 CN_COMMAND 傳回 TButton,又通過一系列的消息傳遞到 TButton 中的 Dispatch,通過查詢動態(tài)方法表找到 Handler -- TButton.CNCommand,它又調(diào)用虛方法 TButton.Click,繼而調(diào)用 TControl.Click,在這個方法中會調(diào)用 FOnClick,而 FOnClick 特性值的內(nèi)容就是當(dāng)程序員使用對象查看器撰寫 TButton 的 OnClick 事件處理函數(shù)時 Delphi 便會自動指定給 TButton 的 OnClick 特性,例子中 OnClick 被指定為 TForm1.Button1Click,因此 TForm1.Button1Click 最終被調(diào)用。

  2. TForm

  新建一個 Application,為 Form1 的 OnMouseDown 事件隨便寫一點代碼,在這個方法上設(shè)斷點,F(xiàn)9 運行,看看 Call Stack

TForm1.FormMouseDown(???,???,[ssLeft],346,212)
TControl.MouseDown(mbLeft,[ssLeft],346,212)
TControl.DoMouseDown((513, 1, 346, 212, (346, 212), 0),mbLeft,[])
TControl.WMLButtonDown((513, 1, 346, 212, (346, 212), 0))
TControl.WndProc((513, 1, 13893978, 0, 1, 0, 346, 212, 0, 0))
TWinControl.WndProc((513, 1, 13893978, 0, 1, 0, 346, 212, 0, 0))
TCustomForm.WndProc((513, 1, 13893978, 0, 1, 0, 346, 212, 0, 0))
TWinControl.MainWndProc((513, 1, 13893978, 0, 1, 0, 346, 212, 0, 0))
StdWndProc(2687598,513,1,13893978)
TApplication.HandleMessage
TApplication.Run
Project1

  鼠標在 Form 上點擊,產(chǎn)生兩個消息 WM_LBUTTONDOWN/WM_LBUTTONUP,但我們只截獲 WM_LBUTTONDOWN。產(chǎn)生的 WM_LBUTTONDOWN 經(jīng)過一系列的消息傳遞到達 TObject.Dispatch,通過查詢動態(tài)方法表在 TForm 的父類 TControl 中找到了 Handler -- TControl.WMLButtonDown,在 TControl.WMLButtonDown 中又經(jīng)過 TControl.DoMouseDown、TControl.MouseDown 一系列方法調(diào)用,最終調(diào)用到 FOnMouseDown,F(xiàn)OnMouseDown 被賦值為 TForm1.FormMouseDown,調(diào)用 FOnMouseDown 即調(diào)用 TForm1.FormMouseDown。
講了一大堆消息實現(xiàn)過程,那么在實際中到底有哪些應(yīng)用?
  四、消息的實際應(yīng)用

  如果你是共享軟件作者,經(jīng)常會為你的軟件被 Crack 掉所煩惱,你能做的就是要加強你的軟件的 Anti-Crack 功能,今天就交你一招。

  如果你用過 Delphi 的專用反匯編工具 DEDE,那么你肯定知道像 Button1Click 這種 Event Handler 的方法入口地址 極容易被定位,其原理是根據(jù)TForm 的 RTTI 信息獲取的(通過分析 dfm 資源文件就可以獲得地址),其實 VCL 窗體只有 published 過的類成員才會生成 RTTI 信息。知道這個關(guān)鍵點加上對 VCL 消息機制的深入了解你就可以防止這一切發(fā)生。

  1. Anti-Crack

  新建一個 Application,在 Form1 上放兩個 Button,命名為 btnRegister、btnCancel,雙擊這兩個按鈕,分別生成TForm1.btnCancelClick、TForm1.btnRegisterClick 兩個 Event Handler 骨架代碼,然后在對象查看器中取消 btnRegister.OnClick 與 TForm1.btnRegisterClick 的關(guān)聯(lián), 隨后將 TForm1.btnCancelClick 的聲明放入 TForms1 聲明的 private 區(qū)段。再按照下面的代碼 內(nèi)容加入其他部分:

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
btnRegister: TButton;
btnCancel: TButton;
procedure btnCancelClick(Sender: TObject);
private
procedure btnRegisterClick(Sender: TObject);
procedure WMCommand(var Message: TWMCommand); message WM_COMMAND;
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnCancelClick(Sender: TObject);
begin
Close;
end;

procedure TForm1.btnRegisterClick(Sender: TObject);
begin
ShowMessage('Thx for ur registration.');
end;

procedure TForm1.WMCommand(var Message: TWMCommand);
begin
if Message.NotifyCode = BN_CLICKED then
if FindControl(Message.Ctl) = btnRegister then
begin
btnRegisterClick(Self);
Exit;
end;
inherited;
end;

end.

  這個方法的本質(zhì)就是截獲 TForm1 的 WM_COMMAND 消息并自己處理,請自行分析代碼,我就不多說了。編譯完后你可以用 DEDE 反匯編一下,看看還能不能那么容易地找到 TForm1.btnRegisterClick 的入口地址。

  結(jié)束語

  VCL 消息機制你理解了嗎?是不是感到特別復(fù)雜?一個消息往往要經(jīng)過10幾個方法才能傳到 Event Handler,別看消息傳遞經(jīng)過這么漫長的路途,但是 VCL 消息機制的效率還是非常高的,因為很多關(guān)鍵的代碼都是用匯編直接寫成的,每一個中途站花費的時間也非常少,因此需要處理的消息還是能很快地到達目的地。

  我最開始學(xué) Windows 編程是從 SDK 開始學(xué)起的,那時候會寫了基本的 Windows 程序,一段時間內(nèi)總認為會 SDK 比會用 Delphi 牛X,現(xiàn)在想起來真傻,比起直來直去的 SDK 編程,VCL 消息機制要復(fù)雜得多得多,看完 VCL 源碼后最大感受就是覺得以前跟沒學(xué)過編程似的,但不可否認的是,只有在你掌握了 OOP/ASM/SDK 這些基礎(chǔ)知識后,你才有看懂 VCL 源碼的資本,這些基礎(chǔ)知識你都掌握了嗎?

溫馨提示:喜歡本站的話,請收藏一下本站!

本類教程下載

系統(tǒng)下載排行

狠狠爱成人网_日韩一级在线_国产综合自拍_亚洲精品韩国_亚洲视频导航_麻豆成人在线播放_欧美jjzz_一区在线视频观看_美脚丝袜一区二区三区在线观看_欧美91视频
6080午夜不卡| 国产一区二区剧情av在线| 亚洲免费资源在线播放| 日韩毛片一二三区| 亚洲www啪成人一区二区麻豆| 午夜精品一区二区三区电影天堂| 美女脱光内衣内裤视频久久网站| 国产成人综合网| 国产精品v欧美精品v日韩| 久久久www| 国产欧美日韩在线| 午夜精品爽啪视频| 一区二区三区成人精品| 91丨九色丨黑人外教| 免费看日韩精品| 亚洲妇女屁股眼交7| 国产精品色婷婷| 久久综合久久综合久久| 欧美二区乱c少妇| 在线亚洲美日韩| 欧美激情在线看| 久久99精品久久久久久| www久久久久| 奇米四色…亚洲| 日韩欧美在线不卡| 91亚洲永久精品| 亚洲免费不卡| 精品国产一区二区三区av性色 | 久久久美女毛片| 一区二区三区中文字幕精品精品| 免费成人在线观看视频| 91蝌蚪国产九色| 在线成人高清不卡| 亚洲一区二区三区影院| 成人精品一区二区三区中文字幕| 在线观看亚洲a| 奇米四色…亚洲| 在线一区二区观看| 精品在线视频一区| 91精品国模一区二区三区| 奇米精品一区二区三区在线观看一| 国产一区二区精品| 亚洲自拍偷拍麻豆| 国产精品视频福利| 日韩成人av影视| 欧美日韩在线精品一区二区三区激情| 一区二区三区在线观看网站| 黄色国产精品一区二区三区| 午夜精品久久久久久不卡8050| 99久久精品免费| 国产精品久久久久三级| 久久综合图片| 国内精品美女在线观看| 亚州成人在线电影| 国产精品久久毛片a| 欧美日韩精品一区视频| 欧美一区激情视频在线观看| 亚洲视频 欧洲视频| 欧美一a一片一级一片| 91丨九色丨蝌蚪丨老版| 日日噜噜夜夜狠狠视频欧美人| 91麻豆精品久久久久蜜臀| 亚洲视频碰碰| 国产精品自在在线| 亚洲一区二区在线视频| 欧美一区二区日韩一区二区| 国产精品日韩久久久| 99久久99久久综合| 日本不卡视频在线| 亚洲欧美日本在线| 欧美大度的电影原声| 一本色道**综合亚洲精品蜜桃冫| 国内揄拍国内精品久久| 激情综合网天天干| 亚洲自拍偷拍av| 国产精品欧美极品| 日韩欧美中文一区| 欧美性色综合网| 99视频精品免费观看| 91免费视频网| 不卡视频在线观看| 国产黄色91视频| 久久国产精品免费| 免费精品视频最新在线| 亚洲国产中文字幕在线视频综合| 久久精品夜夜夜夜久久| 精品精品国产高清一毛片一天堂| 在线欧美日韩精品| 欧美午夜片在线看| 欧美午夜精品久久久久久超碰| 国产精品综合| 久久精品成人一区二区三区蜜臀| 亚洲第一精品影视| av成人毛片| 久热这里只精品99re8久| 午夜在线精品| 一本到一区二区三区| 欧美日韩欧美一区二区| 欧美一区二区观看视频| 国产亚洲欧美激情| 综合久久久久久| 日韩精品一二三| 国产精品亚洲午夜一区二区三区 | 久久精品欧美一区二区三区麻豆| 久久综合色婷婷| 最新热久久免费视频| 亚洲一区二区不卡免费| 久久不见久久见免费视频7| 国产精品中文欧美| 亚洲视频狠狠| 欧美在线观看视频在线| 久久综合网色—综合色88| 亚洲欧美一区二区三区国产精品 | 一区二区三区蜜桃| 国产一区二区按摩在线观看| 欧美国产三级| 在线一区二区三区做爰视频网站| 亚洲视频在线一区二区| 亚洲一级不卡视频| 国产成人啪免费观看软件| 99视频+国产日韩欧美| 精品久久久久一区| 日本最新不卡在线| 国内精品**久久毛片app| 欧美在线一区二区| 亚洲一区二区中文在线| 成人性生交大片免费看视频在线| 蘑菇福利视频一区播放| 国产欧美视频在线观看| 粉嫩av一区二区三区在线播放| 一区二区三区四区五区在线| 国产日韩欧美综合在线| 亚洲色图一区二区三区| 日本不卡一区二区三区| 国产精品538一区二区在线| 国产精品亚洲产品| 久久se这里有精品| 91.xcao| 91蜜桃免费观看视频| 久久男人中文字幕资源站| 国产成人一级电影| 欧美成人伊人久久综合网| 国产伦精品一区二区三区免费迷 | 亚洲男帅同性gay1069| 好吊日精品视频| 国产精品午夜免费| 午夜精品剧场| 中文字幕一区在线观看视频| 99精品国产在热久久婷婷| 婷婷中文字幕一区三区| 欧美日韩精品一区二区三区四区 | 妖精视频成人观看www| 日本中文字幕不卡| 精品理论电影在线| 亚洲人成人一区二区三区| 亚洲综合清纯丝袜自拍| 欧美日韩电影在线| 国产69精品久久久久777| 国产午夜精品一区二区| 影音先锋久久久| 亚洲午夜电影网| 精品捆绑美女sm三区| 999亚洲国产精| 国产精品一级黄| 成人欧美一区二区三区在线播放| 91电影在线观看| 91蜜桃视频在线| 麻豆精品一区二区三区| 国产精品久久久久久妇女6080| 欧美亚洲一区| 色综合一个色综合| 奇米色777欧美一区二区| 久久综合狠狠综合久久激情| 久久久久久久欧美精品| www.66久久| av在线不卡电影| 老司机免费视频一区二区三区| 国产欧美一区二区精品性| 久久综合久久久久88| 香蕉成人久久| 国产成人精品影视| 日韩av电影免费观看高清完整版| 精品成人一区二区三区| 久久综合图片| 日韩一区二区免费看| 欧美精品不卡| 91影视在线播放| 激情小说亚洲一区| 美女在线观看视频一区二区| 亚洲精品国久久99热| 中文字幕的久久| 日韩女优制服丝袜电影| 欧美精品在线观看播放| 欧美亚一区二区| 欧美午夜影院一区| 另类亚洲自拍| 色88888久久久久久影院按摩| 国产欧美一区二区三区另类精品 | 欧美日韩一区不卡| 91福利精品第一导航|