Delphi彙編級初探

來源:互聯網
上載者:User

 

----- 老鰓 --------  考慮如下這個簡單類ttest
unit Unit1;interfaceuses
Windows, SysUtils, Variants, Classes;type
ttest = class
public
j:integer;
i:integer;
function aa(b,c: integer):integer;stdcall;
end;implementationfunction ttest.aa(b,c: integer):integer;stdcall;
begin
Result := b + i + c;
end;end.調用代碼如下var a:ttest;
j:integer;
begin
a := ttest.Create;
a.i := 50;
j:= a.aa(10,20);
end;一。觀察j := a.aa(10,20)的編譯結果:
[要點]:
按stdcall調用傳參數方式,從右至左將參數壓棧,因為是對象的函數調用,
所以最後將對象的地址壓棧,然後調用方法.

二。觀察aa成員函數的編譯結果:
[要點]:1.對象地址擷取:[ebp+$08],即最後一個壓棧的參數(stdcall,其他調用
方式根據壓棧順序可以同理計算出來)2.成員變數值的擷取方法,i的位移是8,因為是第二個整型數.三。根據上面的分析,可以用彙編實現aa成員函數如下:

{用彙編實現該函數如下}
function ttest.aa(b,c: integer):integer;stdcall;
asm
mov eax,[ebp+$0c] //Result := b
mov edx,[ebp+$08] //擷取對象/self地址 -> edx
add eax,[edx+i] //加上成員變數i的值(i在此為相對於self的位移:
//Result := Result + i;
add eax,[ebp+$10] //Result := Result + c;
end;

[要點]:

1.Delphi過程/函數內嵌彙編中只有eax/ecx/edx可以隨意使用,eax一般預設
作為函數的傳回值存放寄存器.
2.其它寄存器要在過程/函數內使用時,最好先壓棧,退出前還原.

 

 

----- 老鰓 --------     本節將來詳細研究一下DELPHI的事件機制,事件在底層實踐上說白了就是過程/函數地址的擴充,一般過程/函數指標儲存的就是純粹的4個位元組(32位作業系統)的過程/函數地址,對比如下:   type    TSimpleEvent = procedure(Sender: TObject) of object;    TProcPointer = procedure(Sender: TObject);    從定義上看,差別很明顯,在於事件多了個" of object ",什麼意思呢,因為事件程序往往定義在別的類的成員過程/函數,作為類成員過程/函數,肯定需要對象地址資訊(用來訪問對象成員變數),所以事件資訊中除了過程/函數地址外還需要一個對象地址,如此可以推測事件和一般過程/函數指標的大小應該不一樣,編寫代碼測試一個會發現:     SizeOf(TSimpleEvent) 等於 8;    SizeOf(TProcPointer ) 等於 4;     下面我們就來驗證一下以上的推測:      建立一簡單DELPHI工程,在form1中增加兩個TSimpleEvent事件:
unit Unit1;interfaceuses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;type
  TSimpleEvent = procedure(Sender: TObject) of object;
  TProcPointer = procedure(Sender: TObject);  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    Faa: TNotifyEvent;
    Fcc: TNotifyEvent;    procedure DoEvent(Sender: TObject);
    procedure Setaa(const Value: TNotifyEvent);
    procedure Setcc(const Value: TNotifyEvent);
  public
    property aa: TNotifyEvent read Faa write Setaa;
    property cc: TNotifyEvent read Fcc write Setcc;
  end; var
  Form1: TForm1;
  iEventAddr: Integer; //事件程序地址
  i,j:integer;implementation {$R *.dfm}procedure TForm1.DoEvent(Sender: TObject);
begin
  showmessage('DoEvent');
end; procedure TForm1.FormCreate(Sender: TObject);begin  //設定事件屬性
  aa := DoEvent;
  cc := DoEvent;   asm
    mov eax,offset DoEvent
    mov iEventAddr,eax
  end;   i := integer(@@cc);  //cc事件變數地址!!事件變數前加一個@表示內容
  j := integer(@@aa);  //aa事件變數地址
end; procedure TForm1.Setaa(const Value: TNotifyEvent);
begin
  Faa := Value;
end; procedure TForm1.Setcc(const Value: TNotifyEvent);
begin
  Fcc := Value;
end; end. 在"  j := integer(@@aa);  //aa事件變數地址" 處設定斷點,運行到這裡後,再按F8跳到過程末尾,然後按Ctrl+Alt+C查看j地址處記憶體:        發現兩個事件變數儲存的前4個位元組都和iEventAddr(即事件處理過程DoEvent的地址)相同,即$4520C0,然後看看後4個位元組內容$D51FE0,是否就是當前表單對象地址呢,現在來驗證一下:   按CTRL+F7 查看@Form1 為$455BFC:    然後來看看該記憶體處的內容,果然為$D51FE0:       真相大白了,事件變數儲存的內容果然是過程/函數地址和過程/函數所屬類的對象地址,OK,先研究到這裡,欲知更詳細內幕,且聽下回繼續分解......

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.