程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> .NET實例教程 >> 構造一個通用的回調Thunk.(把回調函數指向對象的方法的辦法)

構造一個通用的回調Thunk.(把回調函數指向對象的方法的辦法)

編輯:.NET實例教程

構造一個通用的回調Thunk.(把回調函數指向對象的方法)
最近又看到了VCL代碼中的MakeObjectInstance函數,實際上是一段WndProc的Thunk代碼.再一次感歎VCL設計之精巧,效率之高.
不喜歡MFC的消息映射方式,MFC的消息映射雖然好理解,但是是采用查表方式效率實在是太低了.VCL的MakeObjectInstance可以
說是VCL Windows系統的靈魂所在,效率極高.
不禁想可不可以實現一個通用的回調函數Thunk呢,可以把所有回調函數都變成對象的方法.
但是MakeObjectInstance實際上是為WndProc特化的.
分析一下回調函數
1.回調函數不過是一個函數指針.
2.盡管回調函數可以是任何調用約定,但絕大多數Win32API的回調函數都是stdcall.(VC中WINAPI,PASCAL,CALLBACK不過是stdcall的宏).
  我們完全可以不考慮其他的調用約定,只考慮stdcall的.
想一下,如果我們對象的方法也是一個stdcall調用約定的方法,那麼和回調函數還差什麼呢?
只差一個參數,第一個參數對象實例的指針,在Delphi,Pascal,Ada中叫Self,C++,Java,C#中叫this.VB中叫ME.
那麼我們只要塞給它這個對象的地址不就行了嗎.好在stdcall約定參數是由右向左傳遞的,也就是說第一個參數是最後傳遞的,又由於stdccall約定
參數全部是由棧傳遞的.所以我們只要把對象指針直接壓入棧中就行了.
但別忽略了一點,
call指令相當於
     Push 返回地址
     Jmp  函數
ret指令相當於
     pop  返回地址
     Jmp  返回地址     
也就是說實際上在調用函數的時候棧頂保留的是返回地址,如果我們直接壓入實例指針的話原來,當跳到函數體中,函數會把返回地址當Self,而Self則
會被當成返回地址,具體會有什麼樣的後果大家自己去想像一下
所以我們做的事情就是彈出返回地址,壓入實例地址,壓入返回地址,跳到對象方法去執行.
實際上我們就是要構造這樣一段代碼當回調用,這段代碼插入對象實例參數到第一個參數,然後跳到對象方法:
     pop    eax            //彈出返回地址到eax
     push   對象實例       //壓入對象實例
     push   eax            //壓入返回地址
     jmp    對應的對象方法 //跳轉到相應的對象方法
具體實現如下    
    
//構造出一段Thunk代碼
//構造出一段Thunk代碼
Function CreateThunk(Obj : TObject; CallBackProc: Pointer):Pointer;
const
  PageSize = 4096;
  SizeOfJmpCode = 5;
type
  TCode = packed record
    Int3: Byte;          //想調試的的時候填Int 3($CC),不想調試的時候填nop($90)
    PopEAX : Byte;       //把返回地址從棧中彈出
    Push: Byte;          //壓棧指令
    AddrOfSelf: TObject; //壓入Self地址,

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved