• <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>
            幽幽
             
            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 幽幽 閱讀(823) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Windows

            <2009年12月>
            293012345
            6789101112
            13141516171819
            20212223242526
            272829303112
            3456789

            常用鏈接

            留言簿(6)

            隨筆分類(35)

            隨筆檔案(51)

            文章分類(3)

            文章檔案(3)

            相冊(cè)

            我的鏈接

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            久久ww精品w免费人成| 亚洲国产成人久久精品影视| 亚洲国产一成久久精品国产成人综合| 亚洲Av无码国产情品久久| 久久久久久精品成人免费图片| 人妻精品久久久久中文字幕69| 日本免费久久久久久久网站| 久久AV高潮AV无码AV| 丁香久久婷婷国产午夜视频| 久久精品卫校国产小美女| 国内精品久久久久影院网站 | 97久久综合精品久久久综合| 久久福利片| 国产成年无码久久久久毛片| 亚洲国产精品成人久久蜜臀| 国产精品美女久久久久 | 好属妞这里只有精品久久| 久久综合九色综合欧美就去吻| 久久国产亚洲精品无码| 久久天天躁夜夜躁狠狠躁2022| 久久精品www| 精品久久久噜噜噜久久久 | 九九99精品久久久久久| 久久久无码精品亚洲日韩京东传媒| 久久99热国产这有精品| 久久亚洲欧美国产精品| 麻豆精品久久久久久久99蜜桃 | 久久91精品国产91久| 精品国产婷婷久久久| 欧美亚洲国产精品久久蜜芽| 国产高潮国产高潮久久久| 久久丫精品国产亚洲av| 国产A三级久久精品| 久久国产亚洲精品| 久久人人添人人爽添人人片牛牛| 久久国产成人午夜aⅴ影院| 狠狠色伊人久久精品综合网| 99久久久久| 国产一区二区三精品久久久无广告| 久久人人爽人人爽人人片av高请| 嫩草伊人久久精品少妇AV|