青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

幽幽
 
posts - 51,  comments - 28,  trackbacks - 0
       為什么類(class)的成員函(member function)數不能作為回調函數(callback function)
首先來看看回調函數有怎樣的特點。windows中,回調函都顯式(explicit)使用CALLBACK修飾符(decorator)修飾(decorated)。實際上CALLBACK就是_stdcall參數傳遞方式(calling convention)的宏定義。MSDN中對__stdcall做了如下定義:

The __stdcall calling convention is used to call Win32 API functions. The callee cleans the stack, so the compiler makes vararg functions __cdecl. Functions that use this calling convention require a function prototype.


其中心思想是,__stdcall修飾的函數,參數從右至左依次壓入堆棧,被調用者(callee)負責平衡堆棧(clean also called ‘stack unwinding handling’)。



下面來看看類的成員函數有怎樣的特點。在VC++中,所有類的成員函數在定義的時候都被隱式(implicit)定義為__thiscall參數傳遞方式。在MSDN 中對__thiscall做了如下定義:

The __thiscall calling convention is used on member functions and is the default calling convention used by C++ member functions that do not use variable arguments. Under __thiscall, the callee cleans the stack, which is impossible for vararg functions. Arguments are pushed on the stack from right to left, with the this pointer being passed via register ECX, and not on the stack, on the x86 architecture.


其中心思想是,__thiscall 修飾的函數參數從右至左依次壓入堆棧,被調用者負責平衡堆棧。之后是與C語言所有參數傳遞方式均不相同的一點:成員函數所在類的this指針被存入ecx寄存器(這個特性只針對Intel x86架構)。

對比之后,我們發現類成員函數不能作為回調函數的主要原因在于類成員函數使用__thiscal參數傳遞方式,因此需要調用者(caller)通過ecx寄存器提供類對象的指針。而回調函數使用__stdcall參數傳遞方式,不具備這個特點。

如何讓類成員函數成為回調函數
根據第一節對回調函數與類成員函數各自特點的分析。不難發現,只要能想辦法在類成員函數被調用之前設置好ecx寄存器,就能在__stdcall調用的基礎上模擬出一個完好的__thiscall調用。

如何提前設置ecx寄存器呢?我們知道函數調用實際是通過匯編指令(oprand)’call 函數地址’完成的。因此我們可以提供一個中間函數。當回調發生時,先調用中間函數,再在中間函數執行過程中設置ecx寄存器,當ecx設置好后jmp到類成員函數去(注意:這里是jmp不是call)。當執行到類的成員函數時,函數上下文(function context)就和__thiscall所產生的完全一樣了。

如何制作這個中間函數呢?普通的函數是不行的。主要因為在vc++ debug版本的代碼中要使用ecx寄存器做堆棧溢出檢測(stack overflow detect),即使是空函數都是如此。其次由于存在棧框(stack frame)效率也不高。

這時就需要使用thunk來達到我們的目的。所謂thunk就是程序自己生成并執行的一小段匯編代碼。下面通過代碼來理解thunk。

  1#include "windows.h"
  2#include "stdio.h"
  3#include "stdlib.h"
  4#include "assert.h"
  5#include "stdafx.h"
  6//////////////////////////////////////////////////////////////////////////
  7// 回調函數類型定義
  8typedef int (CALLBACK pfaCallBack)(intlongchar);
  9//////////////////////////////////////////////////////////////////////////
 10// thunk 結構定義
 11// 由于thunk 要被當作代碼來執行,因此thunk 結構必須是字節對齊的,這里使用
 12// VC++ 的修飾符號#pragma pack(push, 1) 來定義一個字節對齊的結構體
 13// 之后通過#pragma(pop) 恢復默認對齊模式
 14#pragma pack(push, 1)
 15struct Thunk
 16{
 17     BYTE op_movecx;
 18     DWORD_PTR val_ecx;
 19     BYTE op_call;
 20     DWORD_PTR val_address;
 21}
;
 22#pragma pack(pop)
 23//////////////////////////////////////////////////////////////////////////
 24// 一個類的定義,就這樣平靜的開始了
 25class Dummy {
 26// 一個成員變量
 27private:
 28     int m_id ;
 29// 定義一個thunk
 30private:
 31     Thunk m_thunk;
 32// 定義構造函數,在構造函數中設置m_id值
 33public:
 34     Dummy(int id):m_id(id)
 35     {
 36     }

 37//////////////////////////////////////////////////////////////////////////
 38// 定義一個回調函數,另外他還是個類的成員函數呢
 39public:
 40     int memberCallback(int intVal, long longVal, char charVal)
 41     {
 42         // 做自己想做的事情
 43         printf("\nI am a member function of class Dummy""(Dummy::memberCallback),ID = %d.""\nI got the value 0x%08x 0x%08x \'%c\'"
 44             , m_id, intVal, longVal, charVal);
 45         return m_id;
 46     }

 47//////////////////////////////////////////////////////////////////////////
 48// 初始化thunk 的數據,這里是關鍵
 49public:
 50     void InitThunk()
 51     {
 52         // 0xB9是‘mov ecx, 數值’的機器碼,xB9之后的個字節(32位)指定了要
 53         // 給ecx的數值.
 54         m_thunk.op_movecx = 0xB9;
 55         // 填寫要給ecx的數值為this(類對象的指針)
 56         m_thunk.val_ecx = (DWORD_PTR)this;
 57         // 0xE9是‘jmp 相對地址’的機器碼。相對地址由xE9之后的個字節(32位)
 58         // 給出。
 59         m_thunk.op_call = 0xE9;
 60         // 獲得Dummy::memberCallback的具體地址。關于成員函數與類對象的關系
 61         // 請參閱Stan Lippman 的<<Inside C++ Object Model>>
 62         // 用匯編獲得地址省去了用C++帶來的難看的語法
 63         DWORD_PTR off = 0;
 64        _asm
 65         {
 66                 mov eax, Dummy::memberCallback          
 67                 mov DWORD PTR [off], eax
 68         }

 69         // jmp后面是相對地址,因此要求出這個地址
 70         // 相對地址=成員函數地址-跳轉點下一指令地址
 71         // 正負號不要緊,jmp自己能根據正負判斷如何跳。
 72         m_thunk.val_address = 
 73             off - ( (DWORD_PTR)(&m_thunk.val_address) + sizeof(DWORD_PTR) );
 74     }

 75//////////////////////////////////////////////////////////////////////////
 76// 返回thunk的地址給要回調他的函數。
 77// 那個函數還以為thunk是一個函數地址呢。根本不知道thunk是我們自己構造的
 78// 數據
 79public:
 80     pfaCallBack GetStaticEntry()
 81     {
 82         return (pfaCallBack)&m_thunk;
 83     }

 84}
;
 85//////////////////////////////////////////////////////////////////////////
 86// 一個調用回調函數的函數
 87void Trigger(pfaCallBack callback)
 88{
 89     assert(callback);
 90     int intVal = 0x1234;
 91     int longVal = 0x5678ABCD;
 92     int charVal = 'D';
 93     // 函數內部
 94     int r;
 95     // 開始回調
 96     r = callback(intVal, longVal, charVal);
 97     printf("\n Return value = %d\n", r);
 98}

 99//////////////////////////////////////////////////////////////////////////
100// 傳說中的主函數。VC++工程里生成的就叫_tmain不叫main。
101int _tmain(int argc, _TCHAR argv[])
102{
103     //生成一個對象
104     Dummy *dummy1 = new Dummy(9);
105     //初始化thunk
106     dummy1->InitThunk();
107     //取得thunk地址
108     pfaCallBack pCallback1 = dummy1->GetStaticEntry();
109     //給需要回調函數的函數傳遞thunk
110     Trigger(pCallback1);
111     // 按任意鍵繼續
112     system("pause");
113     return 0;
114}

115
posted on 2008-07-06 03:58 幽幽 閱讀(840) 評論(0)  編輯 收藏 引用 所屬分類: Windows

<2009年3月>
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234

常用鏈接

留言簿(6)

隨筆分類(35)

隨筆檔案(51)

文章分類(3)

文章檔案(3)

相冊

我的鏈接

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲日韩欧美一区二区在线| 欧美日本一区| 亚洲美女福利视频网站| 亚洲二区在线| 亚洲国产第一页| 亚洲三级国产| 亚洲天堂av在线免费观看| 中日韩美女免费视频网址在线观看 | 亚洲永久精品国产| 午夜一区在线| 免费日韩视频| 欧美午夜精品久久久久久浪潮| 国产精品久久久久久av下载红粉 | 亚洲一区三区视频在线观看| 午夜一区二区三区不卡视频| 久久亚洲精品一区| 国产精品videossex久久发布| 欧美成人综合一区| 国产精品中文字幕欧美| 国产一区在线观看视频| 亚洲国产欧美精品| 亚洲一区二区在线观看视频| 欧美专区在线| 亚洲精品一二区| 久久av老司机精品网站导航| 欧美国产日韩亚洲一区| 国产精品色婷婷| 国内精品久久久久久影视8 | 久久av资源网站| 亚洲午夜av电影| 亚洲国产一区二区三区青草影视| 国产精品99久久久久久久女警| 香蕉久久一区二区不卡无毒影院| 欧美a级在线| 亚洲小视频在线观看| 欧美在线精品一区| 久久伊人精品天天| 久久亚洲精品视频| 亚洲国产精品久久久久秋霞影院 | 国产精品www| 国产一区999| 亚洲精品一区二区三区蜜桃久| 亚洲视频观看| 久久久久久久一区二区三区| 亚洲欧洲日韩综合二区| 日韩视频免费在线观看| 欧美承认网站| 亚洲承认在线| 久久久亚洲人| 亚洲精品在线免费| 久久国产精品久久国产精品| 欧美另类综合| 亚洲欧美中文字幕| 欧美精品一区在线发布| 国产精品国产a| 好吊日精品视频| 美日韩精品免费观看视频| 国产精品国产a级| 在线视频亚洲欧美| 亚洲国产成人av好男人在线观看| 欧美一级午夜免费电影| 国产精品亚洲аv天堂网 | 欧美日韩亚洲国产精品| 日韩午夜剧场| 亚洲精品免费网站| 欧美区亚洲区| 亚洲午夜高清视频| 亚洲最新在线| 国产九九视频一区二区三区| 久久国产一区| 久久综合国产精品| 亚洲理论在线| 一区二区三区精密机械公司| 国产精品久久久久aaaa樱花| 欧美一进一出视频| 久久久999精品视频| 亚洲国产一二三| 亚洲美女福利视频网站| 欧美午夜理伦三级在线观看| 欧美亚洲午夜视频在线观看| 欧美一区二区播放| 在线看欧美日韩| 日韩天堂av| 国产在线精品自拍| 亚洲国产天堂久久综合| 欧美日韩在线播放一区二区| 久久黄色级2电影| 欧美99在线视频观看| 亚洲综合国产| 久久亚洲私人国产精品va| 亚洲精品国产视频| 中日韩美女免费视频网址在线观看 | 亚洲黄色免费网站| 欧美特黄视频| 美女视频一区免费观看| 欧美精品激情blacked18| 欧美一区二区性| 欧美国产大片| 久久人91精品久久久久久不卡| 欧美高清不卡在线| 久久国产天堂福利天堂| 欧美精品福利在线| 麻豆av一区二区三区| 国产精品户外野外| 欧美激情视频在线播放| 国产亚洲人成a一在线v站| 亚洲黄一区二区| 国产曰批免费观看久久久| 99国产精品久久久久久久成人热| 国产婷婷一区二区| 洋洋av久久久久久久一区| 欧美国产另类| 久久九九精品| 欧美性做爰毛片| 亚洲国产专区| 影音先锋亚洲电影| 亚洲制服欧美中文字幕中文字幕| 亚洲精品九九| 久久亚洲色图| 裸体歌舞表演一区二区| 国产精品亚洲综合久久| 亚洲精品黄网在线观看| 亚洲黄色有码视频| 久久久亚洲午夜电影| 久久不射中文字幕| 国产精品无码专区在线观看| 夜夜嗨av一区二区三区| 一区二区国产日产| 欧美日韩精品综合| 日韩视频二区| 一本久道久久综合婷婷鲸鱼| 欧美成人在线网站| 欧美电影免费网站| 亚洲国产精品国自产拍av秋霞| 久久成人精品无人区| 久久久高清一区二区三区| 国产免费一区二区三区香蕉精| 亚洲一区免费观看| 久久av红桃一区二区小说| 国产精品专区h在线观看| 亚洲女同同性videoxma| 久久精品1区| 在线不卡欧美| 久久一区国产| 亚洲高清视频在线观看| 亚洲精品一区二区网址| 欧美精品一区二区蜜臀亚洲| 亚洲国产精品ⅴa在线观看| 日韩天堂在线视频| 欧美偷拍另类| 欧美在线观看日本一区| 久久综合狠狠综合久久激情| 永久域名在线精品| 免费亚洲一区二区| 日韩视频三区| 欧美一级午夜免费电影| 国产精品一区二区在线观看网站 | 亚洲精选中文字幕| 欧美视频福利| 欧美在线免费视屏| 欧美激情视频免费观看| 亚洲一本大道在线| 久久不见久久见免费视频1| 久久国产婷婷国产香蕉| 欧美国产精品中文字幕| 亚洲美女尤物影院| 国产精品久久午夜| 久久www免费人成看片高清| 欧美黄色成人网| 亚洲一线二线三线久久久| 国产亚洲视频在线观看| 欧美激情精品久久久久久免费印度| 日韩午夜高潮| 一区二区三区导航| 欧美高清视频一区| 欧美日韩精品免费观看视频| 亚洲视频999| 欧美暴力喷水在线| 亚洲欧美激情四射在线日 | 一区二区三区亚洲| 欧美欧美在线| 久久国产精品一区二区三区四区| 欧美成人免费va影院高清| 亚洲免费视频在线观看| 最新亚洲视频| 激情婷婷亚洲| 国产欧美日韩中文字幕在线| 欧美成人资源网| 午夜精品久久久久99热蜜桃导演| 亚洲国产精品va在线看黑人| 久久久人成影片一区二区三区| 亚洲系列中文字幕| 亚洲精品综合| 精品99视频| 国产欧美在线播放| 国产精品日韩欧美| 欧美日韩不卡视频| 欧美激情中文字幕一区二区| 久久久久久亚洲精品杨幂换脸| 亚洲一区图片|