国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

VCL源碼分析方法論

2019-11-18 18:28:12
字體:
來源:轉載
供稿:網友
 最近一段時間似乎流行源碼分析:)我也來談談在過去一段時間里對VCL源碼的分析方法方面的一點體會,本文將不探討VCL類庫的構架和設計模式方面的東本,只是以我們常見的控件屬性/方法的實現過程作簡單的說明,希望對初學者有所幫助

VCL分析方法
例:TButton.Caption屬性的由來
(本文僅以此獻給DELPHI初學者)
   用過一段時間DELPHI的朋友,都會對VCL源碼感興趣。本人也常常在各大論壇見到一些網友研究討論過關于VCL源碼的貼子。不過,很多網友很努力的想看懂,可最后還是半途而廢,因為他們總是理不出個頭緒、看得云里霧里。筆者我也有看源碼的習慣,沒事的時候就點點鼠標右鍵,總是希望得到一些僥幸的收獲和開發技巧。
   不過萬事都得先有個基本前題,就像人上學的過程一樣(這里指正常人)要按部就班的來,一般不可能小學一畢業就直接去念大學,除非他(她)是個天才或經過特別培訓。所以各位GGJJDDMM,看VCL源碼也是有個基本前題的,首先你得熟悉WIN32 API/SDK,如果你說不知道的話,可以參考書籍《PRogramming Windows》(中文名《WINDOWS 程序設計》)。其次是你應當對Object Pascal比較熟悉,或者你曾經對DELPHI的組件進行過擴展(做過組件開發),那么我相信你對Object Pascal已經熟悉。不熟也不要緊,DELPHI的在線幫助就有對Object Pascal的講述,如果英文太差也不要緊,網上也有很多熱心網友翻譯過來的中文幫助和語言參考書。
呵呵,本人寫技術文章就像在寫散文:)
   言歸正傳,我們這篇文章的主題是對VCL源碼的分析,分析當然有一個分析方法的問題,總不能隨便打開一個源程序,逮著一個函數就分析一個函數吧:)所以我們也應該有選擇,有目的的分析。
想想我們每天編碼時都會遇到的屬性有哪些?呵呵,NAME,CAPTION,VISIBLE,還有一些控件的TEXT(如EDIT1.TEXT)。那么我們就以控件的CAPTION來分析吧。
當然不是每個控件都有CAPTION屬性的,我們這里就用TButton類的Caption屬性進行分析。
   打開每天我們都會使用的DELPHI,在FORM窗體上放一個按鈕,得到一個Button1的按鈕控件,按F12打天源程序,有沒有找到這段代碼呢:
Button1: TButton;
對了,在TButton上點擊鼠標右鍵,在彈出的上下文菜單中選擇第一項Find Declaration,找到TButton類的定義,如下所示:
 TButton = class(TButtonControl)
 private
   FDefault: Boolean;
   FCancel: Boolean;
   FActive: Boolean;
   FModalResult: TModalResult;
   procedure SetDefault(Value: Boolean);
。。。。。。

   原來TButton繼承于TButtonControl類,呵呵:)
在左邊的對象窗口(Exploring Unit.pas窗口)中找到TButton的CAPTION屬性,如下圖:

   雙擊CAPTION屬性,找到定義CAPTION屬性的源碼,大家可能發現什么都沒有,只有一個   
   property Caption;
   呵呵,寫過組件的朋友都知道,按理Caption屬性應該有讀/寫文本的方法啊?在哪里去了呢,呵呵,這里沒有出現,當然應該在它的父類里了(這里只是申明Caption出來的地方),我們順著剛才的方法繼續在TButtonControl,發現也沒有,最終我們在TControl類里找到了這個CAPTION,至于為什么是protected成員,我就不多說了:
 protected
   procedure ActionChange(Sender: TObject; CheckDefaults: Boolean); dynamic;
   procedure AdjustSize; dynamic;
   procedure AssignTo(Dest: TPersistent); override;
   procedure BeginAutoDrag; dynamic;
   function CanResize(var NewWidth, NewHeight: Integer): Boolean; virtual;
   function CanAutoSize(var NewWidth, NewHeight: Integer): Boolean; virtual;
   procedure Changed;
   procedure ChangeScale(M, D: Integer); dynamic;
。。。。。。
   property Caption: TCaption read GetText write SetText stored IsCaptionStored;
   看看GetText、SetText就是操作文本屬性的函數了,我們找到GetText、SetText定義如下:
function GetText: TCaption;
procedure SetText(const Value: TCaption);
還有TCaption,它的定義居然是一個自定義類型:
TCaption = type string;
   說明GetText返回值和SetText的調用參數本來也就是一個string型的:)

   下面我們來看看GetText源碼:
function TControl.GetText: TCaption;
var
 Len: Integer;
begin
 Len := GetTextLen;//得到文本長度
 SetString(Result, PChar(nil), Len);// 設置Result返回以Len指定的長度
 if Len <> 0 then GetTextBuf(Pointer(Result), Len + 1);//長度不為空,Result得到文本數據
end;

   如果不明白GetTextBuf的用法,看看如下的代碼:
procedure TForm1.Button1Click(Sender: TObject);
var
 Buffer: PChar;
 Size: Byte;
begin
 Size := Edit1.GetTextLen;   //得到EDIT1的文本長
 Inc(Size);               
 GetMem(Buffer, Size);          //創建EDIT1文本長度大小的緩存空間
 Edit1.GetTextBuf(Buffer,Size);  //由緩存得到文本,Buffer里的值就是Edit1.Text
 Edit2.Text := StrPas(Buffer);   //Buffer轉換為PASCAL字符類型數據
 FreeMem(Buffer, Size);      //釋放內存
end;
以上程序的行為同以下程序相當:
procedure TForm1.Button1Click(Sender: TObject);
begin
 Edit2.Text := Edit1.Text;
end;

   回到GetText函數,其中GetTextLen的作用是得到文本長度,GetTextBuf得到文本數據。
   SetText就更簡單了,定義如下:
procedure TControl.SetText(const Value: TCaption);
begin
 if GetText <> Value then SetTextBuf(PChar(Value));
end;
   意思是如果設定的Value與原來的不同,則重新設置緩存文本。



   為了更深入VCL底部,我們再看看GetTextLen如何實現的(其實SetTextBuf和GetTextLen的實現過程相似):
function TControl.GetTextLen: Integer;
begin
 Result := Perform(WM_GETTEXTLENGTH, 0, 0);//WM_派發的是WINDOWS標準消息
end;
   看到這里想必大家都明白了,如果還不明白(沒用過Perform),我看再看看Perform,它到底做了什么:
function TControl.Perform(Msg: Cardinal; WParam, LParam: Longint): Longint;
var
 Message: TMessage;
Begin
{你的消息賦予TMessage }
 Message.Msg := Msg; ;
 Message.WParam := WParam;
 Message.LParam := LParam;
Message.Result := 0;//0表示返回不處理
 if Self <> nil then WindowProc(Message);//不為空,將消息交給TControl的窗口過程WindowProc處理
 Result := Message.Result;//返回結果
end;
   這里主要再看看WindowProc做了什么,TControl里面WindowProc是這樣定義的:
property WindowProc: TWndMethod read FWindowProc write FWindowProc;
在TControl的Create函數中:
constructor TControl.Create(AOwner: TComponent);
begin
 inherited Create(AOwner);
 FWindowProc := WndProc;
。。。。。。
   可見我們還要找到TControl 的WndProc過程才能明白究竟,
WndProc過程定義如下:
procedure WndProc(var Message: TMessage); override;
   實現:
procedure TControl.WndProc(var Message: TMessage);
var
 Form: TCustomForm;
 KeyState: TKeyboardState; 
 WheelMsg: TCMMouseWheel;
begin
 if (csDesigning in ComponentState) then
 begin
   Form := GetParentForm(Self);
   if (Form <> nil) and (Form.Designer <> nil) and
     Form.Designer.IsDesignMsg(Self, Message) then Exit
 end;
 if (Message.Msg >= WM_KEYFIRST) and (Message.Msg <= WM_KEYLAST) then
 begin
   Form := GetParentForm(Self);
   if (Form <> nil) and Form.WantChildKey(Self, Message) then Exit;
 end
 else if (Message.Msg >= WM_MOUSEFIRST) and (Message.Msg <= WM_MOUSELAST) then
 begin
   if not (csDoubleClicks in ControlStyle) then
     case Message.Msg of
       WM_LBUTTONDBLCLK, WM_RBUTTONDBLCLK, WM_MBUTTONDBLCLK:
         Dec(Message.Msg, WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
     end;
   case Message.Msg of
     WM_MOUSEMOVE: application.HintMouseMessage(Self, Message);
     WM_LBUTTONDOWN, WM_LBUTTONDBLCLK:
       begin
         if FDragMode = dmAutomatic then
         begin
           BeginAutoDrag;
           Exit;
         end;
         Include(FControlState, csLButtonDown);
       end;
     WM_LBUTTONUP:
       Exclude(FControlState, csLButtonDown);
   else
     with Mouse do
       if WheelPresent and (RegWheelMessage <> 0) and
         (Message.Msg = RegWheelMessage) then
       begin
         GetKeyboardState(KeyState);
         with WheelMsg do
         begin
           Msg := Message.Msg;
           ShiftState := KeyboardStateToShiftState(KeyState);
           WheelDelta := Message.WParam;
           Pos := TSmallPoint(Message.LParam);
         end;
         MouseWheelHandler(TMessage(WheelMsg));
         Exit;
       end;
   end;
 end
 else if Message.Msg = CM_VISIBLECHANGED then
   with Message do
     SendDockNotification(Msg, WParam, LParam);
 Dispatch(Message);//派發消息
end;
   這里主要講講Dispatch方法,它根據傳入的消息調用消息的句柄方法,如果在組件類和它的父類都沒有找到消息的處理句柄,Dispatch方法便會調用Defaulthandler(默認的消息處理方法),如下:
procedure TObject.Dispatch(var Message);
asm
   PUSH    ESI
   MOV     SI,[EDX]
   OR      SI,SI
   JE      @@default
   CMP     SI,0C000H
   JAE     @@default
   PUSH    EAX
   MOV     EAX,[EAX]
   CALL    GetDynaMethod
   POP     EAX
   JE      @@default
   MOV     ECX,ESI
   POP     ESI
   JMP     ECX

@@default:
   POP     ESI
   MOV     ECX,[EAX]
   JMP     DWord PTR [ECX] + VMTOFFSET TObject.DefaultHandler//調用默認的消息處理方法
end;
   而默認的消息處理如下,在SYSTEM.PAS單元里:
procedure TObject.DefaultHandler(var Message);
begin
end;
     由以上代碼看好像是沒有任何處理過程,跟蹤Object.DefaultHandler的匯編執行動作call dword ptr[ecx-$10],即調用Object.DefaultHandle,看看做何處理:
{Object.DefaultHandle}
Ret
Lea eax,[eax+$00]
即一個返回處理!

從最表面的Button.caption,我們走到了編譯器層,可見所有東西都能找到它固有的原點!以caption的分析為基礎,我們可以繼續分析name屬性和其它一些方法/函數。
希望我這篇‘散文’能給大家理出點頭緒:)

上一篇:制作QQ消息炸彈

下一篇:一個實際的OLE服務器的開發和使用

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
學習交流
熱門圖片

新聞熱點

疑難解答

圖片精選

網友關注

主站蜘蛛池模板: 白山市| 许昌县| 赣榆县| 祥云县| 云和县| 普定县| 万安县| 双牌县| 大港区| 苏州市| 广德县| 文昌市| 澄迈县| 缙云县| 华容县| 民县| 开化县| 奉贤区| 安达市| 新宾| 布尔津县| 苏尼特右旗| 阜阳市| 蚌埠市| 丹巴县| 棋牌| 柘荣县| 乌拉特前旗| 北海市| 理塘县| 隆子县| 泸州市| 九龙县| 九江县| 佛学| 乌拉特中旗| 新平| 呼图壁县| 德惠市| 临海市| 佛教|