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

幽幽
 
posts - 51,  comments - 28,  trackbacks - 0
       為什么類(class)的成員函(member function)數(shù)不能作為回調(diào)函數(shù)(callback function)
首先來看看回調(diào)函數(shù)有怎樣的特點(diǎn)。windows中,回調(diào)函都顯式(explicit)使用CALLBACK修飾符(decorator)修飾(decorated)。實(shí)際上CALLBACK就是_stdcall參數(shù)傳遞方式(calling convention)的宏定義。MSDN中對(duì)__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修飾的函數(shù),參數(shù)從右至左依次壓入堆棧,被調(diào)用者(callee)負(fù)責(zé)平衡堆棧(clean also called ‘stack unwinding handling’)。



下面來看看類的成員函數(shù)有怎樣的特點(diǎn)。在VC++中,所有類的成員函數(shù)在定義的時(shí)候都被隱式(implicit)定義為__thiscall參數(shù)傳遞方式。在MSDN 中對(duì)__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 修飾的函數(shù)參數(shù)從右至左依次壓入堆棧,被調(diào)用者負(fù)責(zé)平衡堆棧。之后是與C語言所有參數(shù)傳遞方式均不相同的一點(diǎn):成員函數(shù)所在類的this指針被存入ecx寄存器(這個(gè)特性只針對(duì)Intel x86架構(gòu))。

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

如何讓類成員函數(shù)成為回調(diào)函數(shù)
根據(jù)第一節(jié)對(duì)回調(diào)函數(shù)與類成員函數(shù)各自特點(diǎn)的分析。不難發(fā)現(xiàn),只要能想辦法在類成員函數(shù)被調(diào)用之前設(shè)置好ecx寄存器,就能在__stdcall調(diào)用的基礎(chǔ)上模擬出一個(gè)完好的__thiscall調(diào)用。

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

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

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

  1#include "windows.h"
  2#include "stdio.h"
  3#include "stdlib.h"
  4#include "assert.h"
  5#include "stdafx.h"
  6//////////////////////////////////////////////////////////////////////////
  7// 回調(diào)函數(shù)類型定義
  8typedef int (CALLBACK pfaCallBack)(intlongchar);
  9//////////////////////////////////////////////////////////////////////////
 10// thunk 結(jié)構(gòu)定義
 11// 由于thunk 要被當(dāng)作代碼來執(zhí)行,因此thunk 結(jié)構(gòu)必須是字節(jié)對(duì)齊的,這里使用
 12// VC++ 的修飾符號(hào)#pragma pack(push, 1) 來定義一個(gè)字節(jié)對(duì)齊的結(jié)構(gòu)體
 13// 之后通過#pragma(pop) 恢復(fù)默認(rèn)對(duì)齊模式
 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// 一個(gè)類的定義,就這樣平靜的開始了
 25class Dummy {
 26// 一個(gè)成員變量
 27private:
 28     int m_id ;
 29// 定義一個(gè)thunk
 30private:
 31     Thunk m_thunk;
 32// 定義構(gòu)造函數(shù),在構(gòu)造函數(shù)中設(shè)置m_id值
 33public:
 34     Dummy(int id):m_id(id)
 35     {
 36     }

 37//////////////////////////////////////////////////////////////////////////
 38// 定義一個(gè)回調(diào)函數(shù),另外他還是個(gè)類的成員函數(shù)呢
 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 的數(shù)據(jù),這里是關(guān)鍵
 49public:
 50     void InitThunk()
 51     {
 52         // 0xB9是‘mov ecx, 數(shù)值’的機(jī)器碼,xB9之后的個(gè)字節(jié)(32位)指定了要
 53         // 給ecx的數(shù)值.
 54         m_thunk.op_movecx = 0xB9;
 55         // 填寫要給ecx的數(shù)值為this(類對(duì)象的指針)
 56         m_thunk.val_ecx = (DWORD_PTR)this;
 57         // 0xE9是‘jmp 相對(duì)地址’的機(jī)器碼。相對(duì)地址由xE9之后的個(gè)字節(jié)(32位)
 58         // 給出。
 59         m_thunk.op_call = 0xE9;
 60         // 獲得Dummy::memberCallback的具體地址。關(guān)于成員函數(shù)與類對(duì)象的關(guān)系
 61         // 請(qǐng)參閱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后面是相對(duì)地址,因此要求出這個(gè)地址
 70         // 相對(duì)地址=成員函數(shù)地址-跳轉(zhuǎn)點(diǎn)下一指令地址
 71         // 正負(fù)號(hào)不要緊,jmp自己能根據(jù)正負(fù)判斷如何跳。
 72         m_thunk.val_address = 
 73             off - ( (DWORD_PTR)(&m_thunk.val_address) + sizeof(DWORD_PTR) );
 74     }

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

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

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

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

<2008年7月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

常用鏈接

留言簿(6)

隨筆分類(35)

隨筆檔案(51)

文章分類(3)

文章檔案(3)

相冊(cè)

我的鏈接

搜索

  •  

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            欧美成人国产| 国产精品久久久久影院色老大 | 欧美精品一区二区高清在线观看| 亚洲精品一区二区三区不| 久久久999| 久久久噜噜噜久久久| 久久精品亚洲精品| 欧美 日韩 国产精品免费观看| 久久亚洲精品网站| 91久久久亚洲精品| 亚洲乱码视频| 久久精品日韩| 欧美日韩精品免费观看视一区二区 | 激情欧美丁香| 亚洲国产精品第一区二区三区| 亚洲国产精品成人久久综合一区| 亚洲视频综合在线| 午夜久久福利| 欧美精品三级| 国内精品嫩模av私拍在线观看| 亚洲精品免费一区二区三区| 一区二区三区精品国产| 久久久噜噜噜久久人人看| 一本色道久久综合亚洲精品不| 香蕉久久夜色精品| 欧美久久久久久久久久| 黑人巨大精品欧美黑白配亚洲 | 国产午夜精品在线| 亚洲免费观看在线观看| 久久综合激情| 欧美一区二区三区四区在线观看地址| 免费的成人av| 亚洲大片免费看| 欧美一区二区三区四区在线观看| 亚洲精品一区二| 欧美激情中文字幕乱码免费| 狠狠v欧美v日韩v亚洲ⅴ| 欧美一区二区三区视频免费播放 | 久久久九九九九| 国产自产精品| 久久精品日产第一区二区三区| 亚洲一级在线观看| 国产精品一区二区你懂得 | 亚洲欧洲精品一区二区三区不卡| 欧美在线|欧美| 欧美jizz19hd性欧美| 在线观看的日韩av| 亚洲大胆女人| 欧美日韩另类在线| 亚洲制服av| 久久久91精品| 日韩亚洲欧美高清| 久久久国产亚洲精品| 在线观看亚洲精品视频| 亚洲国产视频一区二区| 国产精品久久久久国产a级| 久久视频在线视频| 欧美性一区二区| 亚洲激情欧美| 亚洲高清资源综合久久精品| 亚洲看片免费| 亚洲国产精品成人综合色在线婷婷 | 国语自产精品视频在线看一大j8 | 久久先锋影音av| 欧美中文字幕视频| 国产精品久久一区二区三区| 亚洲精品在线免费| 黄色欧美日韩| 久久久国产精品亚洲一区 | 亚洲国产成人在线播放| 国产欧美日韩综合| 亚洲男人影院| 午夜老司机精品| 国产精品嫩草久久久久| 在线亚洲精品福利网址导航| 亚洲一级黄色av| 国产精品丝袜白浆摸在线| 亚洲色图自拍| 久久精品国产免费观看| 国产一本一道久久香蕉| 久久精品欧美| 亚洲经典三级| 亚洲一区二区三区激情| 国产精品青草久久| 久久噜噜噜精品国产亚洲综合| 裸体歌舞表演一区二区| 亚洲人被黑人高潮完整版| 国产精品福利在线| 美女日韩欧美| 亚洲香蕉视频| 亚洲黄色在线视频| 久久久亚洲欧洲日产国码αv| 亚洲全黄一级网站| 国产一区二区三区电影在线观看| 麻豆久久婷婷| 欧美亚洲日本国产| 99精品国产在热久久| 亚洲福利视频二区| 久久激五月天综合精品| 免费日韩av片| 久久精品日产第一区二区三区| 一区二区三区www| 亚洲日本国产| 亚洲欧洲精品一区二区三区不卡 | 欧美一站二站| 日韩午夜在线播放| 亚洲日韩欧美视频一区| 能在线观看的日韩av| 久久久久久久久岛国免费| 亚洲欧美制服另类日韩| 亚洲一区二区av电影| 亚洲欧美电影在线观看| 亚洲综合第一页| 一本久道综合久久精品| 亚洲靠逼com| 一区二区91| 亚洲欧美日韩另类| 久久成人亚洲| 欧美成人精品在线播放| 欧美成人资源| 日韩一本二本av| 亚洲欧美文学| 玖玖玖国产精品| 欧美视频久久| 在线观看欧美日韩国产| 亚洲精品在线免费观看视频| 日韩亚洲欧美在线观看| 午夜精品久久久久久久99热浪潮| 欧美一区二区日韩| 欧美电影在线| 亚洲一级片在线看| 麻豆精品视频在线观看| 国产精品美女主播| 亚洲日本久久| 欧美一区二区三区另类| 欧美激情在线| 久久精品国产91精品亚洲| 欧美日韩国产成人在线观看| 国产一区二区高清视频| 一区二区三区毛片| 欧美电影免费网站| 美国三级日本三级久久99| 国产老肥熟一区二区三区| 亚洲一区www| 亚洲三级毛片| 欧美www在线| 亚洲国产精品嫩草影院| 久久国产精品一区二区三区| 一本色道久久综合精品竹菊| 欧美成年人视频| 日韩性生活视频| 亚洲一区二区成人在线观看| 欧美三级午夜理伦三级中视频| 在线观看中文字幕不卡| 老色鬼精品视频在线观看播放| 午夜视频一区在线观看| 国产人成精品一区二区三| 久久九九国产精品怡红院| 香蕉亚洲视频| 亚洲国产精彩中文乱码av在线播放| 麻豆国产精品777777在线| 玖玖综合伊人| 午夜精品久久久久久久99樱桃| 亚洲综合日本| 日韩手机在线导航| 亚洲欧美欧美一区二区三区| 尤物精品国产第一福利三区| 亚洲国产精品一区二区三区| 国产精品一区亚洲| 亚洲高清毛片| 精品福利免费观看| 一区二区三区高清视频在线观看| 国产精品一区二区黑丝| 亚洲国产日韩美| 狠狠综合久久av一区二区老牛| 亚洲日本一区二区三区| 国产日韩欧美精品一区| 日韩一区二区电影网| 亚洲精品国产系列| 久久久精品日韩欧美| 欧美一区不卡| 国产欧美另类| 香港成人在线视频| 欧美一区二区三区视频免费播放 | 制服丝袜亚洲播放| 久久精品视频一| 日韩天堂在线观看| 久久久久久久综合| 欧美在线网址| 国产乱人伦精品一区二区| 国产精品99久久久久久久久| 最新国产の精品合集bt伙计| 久久天天躁狠狠躁夜夜爽蜜月| 欧美在线观看你懂的| 国产日产欧美一区| 久久久久国色av免费观看性色| 久久久精品国产免费观看同学 | 另类尿喷潮videofree| 欧美jjzz| 亚洲网址在线|