狠狠爱成人网_日韩一级在线_国产综合自拍_亚洲精品韩国_亚洲视频导航_麻豆成人在线播放_欧美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视频
噜噜噜久久亚洲精品国产品小说| 国产日韩亚洲欧美精品| 国产一区在线观看麻豆| 亚洲一区二区三区三| 亚洲综合久久久| 亚洲品质自拍视频| 亚洲女同一区二区| 亚洲欧美aⅴ...| 一区二区三区av电影| 亚洲精品网站在线观看| 亚洲在线中文字幕| 亚洲一级在线观看| 视频一区免费在线观看| 日本美女一区二区| 精品在线播放免费| 成人看片黄a免费看在线| 成人午夜av在线| 99热在这里有精品免费| 欧美日韩高清在线一区| 在线观看视频免费一区二区三区| 亚洲精选91| 一本色道久久综合精品竹菊| 欧美怡红院视频| 精品欧美一区二区久久| 欧美高清在线一区二区| 亚洲色图欧洲色图婷婷| 亚洲大片在线观看| 蜜桃一区二区三区在线观看| 国产美女在线观看一区| 欧美国产精品| 亚洲免费综合| 91麻豆精品国产91久久久使用方法| 日韩视频免费观看高清在线视频| 欧美激情中文不卡| 亚洲一区二区三区自拍| 麻豆成人91精品二区三区| 成人免费福利片| 亚洲精品色图| 欧美精品丝袜中出| 国产欧美日产一区| 亚洲一区日韩精品中文字幕| 久久精品av麻豆的观看方式| av一区二区三区| 性欧美暴力猛交另类hd| 3d动漫精品啪啪| 国产精品夫妻自拍| 国产一区二区福利| 黄色亚洲精品| 欧美日韩国产一区二区三区地区| 国产网红主播福利一区二区| 国产成人综合在线观看| 色综合色狠狠综合色| 久久精品午夜| 国产日韩精品视频一区| 天天操天天色综合| 91色|porny| 美女精品在线| 久久久精品日韩欧美| 午夜av一区二区| 国产精品va| 欧美一区日韩一区| 一区二区三区自拍| 国产成人精品影视| 99国产成+人+综合+亚洲欧美| 91精品国产综合久久久久久久久久| 国产精品视频你懂的| 国产一区二区在线观看免费| 亚洲伦伦在线| 国产午夜精品福利| 国产一区二区剧情av在线| 国产亚洲第一区| 日韩视频一区在线观看| 免费观看日韩av| 亚洲美女一区| 国产女主播一区| 成人毛片视频在线观看| 日本精品视频一区二区三区| 亚洲女与黑人做爰| 欧美日韩精品免费观看视频完整| 欧美精品日韩一本| 琪琪久久久久日韩精品| 国产精品免费看| 亚洲人成电影网站色mp4| 欧美成人久久| 精品国产成人在线影院| 国产原创一区二区三区| 色老汉av一区二区三区| 亚洲久草在线视频| 亚洲国产精品第一区二区| 精品黑人一区二区三区久久| 国产一区二区三区视频在线播放| 老司机午夜精品视频在线观看| 亚洲猫色日本管| 欧美亚洲不卡| 中文字幕在线观看一区二区| 欧美xxx在线观看| 国产午夜一区二区三区| 成人免费视频网站在线观看| 91麻豆精品国产自产在线观看一区| 久久激情综合网| 在线精品视频免费播放| 天堂在线一区二区| 日本高清免费不卡视频| 天天综合天天做天天综合| 国产美女精品| 五月婷婷综合在线| 玖玖在线精品| 美腿丝袜一区二区三区| 欧美亚洲日本一区| 精品一区二区三区免费毛片爱| 欧美日韩亚洲不卡| 国产真实乱子伦精品视频| 91精品国产综合久久香蕉的特点| 国产美女av一区二区三区| 6080日韩午夜伦伦午夜伦| 国产999精品久久久久久| 日韩免费观看高清完整版| av午夜精品一区二区三区| 国产日韩欧美高清| 亚洲精品色图| 日韩国产欧美在线观看| 欧美日韩成人一区| av一区二区三区黑人| 国产精品女主播av| 免费日韩精品中文字幕视频在线| 日韩国产成人精品| 日韩一区二区三区av| 欧美高清视频一区二区三区在线观看 | 夜夜揉揉日日人人青青一国产精品| 国产日韩欧美在线播放不卡| 天天综合色天天| 91精品在线观看入口| 91麻豆文化传媒在线观看| 亚洲人成在线播放网站岛国| 久久最新视频| 97精品超碰一区二区三区| 亚洲欧洲国产日本综合| 91九色最新地址| av电影天堂一区二区在线| 一区二区三区在线不卡| 欧美亚洲禁片免费| 欧美理论在线| 奇米影视在线99精品| 精品国产三级电影在线观看| 亚洲无玛一区| 国产一区二区在线电影| 国产精品国产a级| 欧美日韩久久一区| 亚洲婷婷在线| 国产美女精品人人做人人爽| 国产精品传媒视频| 欧美吻胸吃奶大尺度电影| 女人色偷偷aa久久天堂| 日韩国产欧美三级| 中文在线免费一区三区高中清不卡| 久久综合狠狠综合久久综青草| 成人av在线电影| 午夜精品福利一区二区蜜股av | 日本在线播放一区二区三区| 精品久久人人做人人爰| 美女久久一区| 欧美日韩在线播放一区二区| 久久精品国产第一区二区三区| 国产精品成人午夜| 制服丝袜一区二区三区| 亚洲一区二区三区四区中文| www.日韩av| 狠狠色狠狠色综合| 亚洲裸体xxx| 久久精品视频一区二区| 欧美婷婷六月丁香综合色| 亚洲黄网站黄| 91视频国产观看| 国产自产2019最新不卡| 亚洲伊人伊色伊影伊综合网| 国产日产欧美精品一区二区三区| 欧美日韩激情一区二区三区| 国产深夜精品| 精品av久久久久电影| av亚洲精华国产精华精| 国产一区在线观看麻豆| 日韩成人av影视| 一区二区不卡在线视频 午夜欧美不卡在| 欧美成人在线直播| 欧美精品vⅰdeose4hd| 香蕉av777xxx色综合一区| 伊人久久婷婷| 欧美日韩综合| 91热门视频在线观看| 国产91在线观看| 国产精品99久久久久| 紧缚奴在线一区二区三区| 日本欧美一区二区| 五月天欧美精品| 亚洲电影一级片| 一片黄亚洲嫩模| 亚洲一卡二卡三卡四卡| 夜夜嗨av一区二区三区四季av| 亚洲欧美激情在线| 国产精品国产自产拍高清av|