直接调用类成员函数地址(用汇编取类成员函数的地址,各VS版本还有所不同)

在C++中,成员函数的指针是个比较特殊的东西。对普通的函数指针来说,可以视为一个地址,在需要的时候可以任意转换并直接调用。但对成员函数来说,常规类型转换是通不过编译的,调用的时候也必须采用特殊的语法。C++专门为成员指针准备了三个运算符: "::*"用于指针的声明,而"->*"和".*"用来调用指针指向的函数。

// Thunk.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

typedef unsigned int DWORD;

//取类成员函数的地址.vc8版本.可以取私有成员函数地址.
#define GetMemberFuncAddr_VC8(FuncAddr,FuncType)\
{                                                \
     __asm                                        \
    {                                            \
        mov eax,offset FuncType                    \
    };                                            \
    __asm                                        \
    {                                            \
        mov FuncAddr, eax                        \
    };                                            \
}

//取类成员函数的地址.vc6版本.
template <class ToType, class FromType>
void GetMemberFuncAddr_VC6(ToType& addr,FromType f)
{
    union 
    {
        FromType _f;
        ToType   _t;
    }ut;

    ut._f = f;

    addr = ut._t;
}

//调用类成员函数
DWORD CallMemberFunc(int callflag,DWORD funcaddr,void *This,int count,...)
{
    DWORD re;

    if(count>0)//有参数,将参数压入栈.
    {
        __asm
        {
            mov  ecx,count;//参数个数,ecx,循环计数器.

            mov  edx,ecx;
            shl  edx,2;    
            add  edx,0x14;  edx = count*4+0x14;

      next:    push  dword ptr[ebp+edx];
            sub   edx,0x4;
            dec   ecx  
            jnz   next;
        }
    }

    //处理this指针.
    if(callflag==0) //__thiscall,vc默认的成员函数调用类型.
    {
        __asm mov ecx,This;
    }
    else//__stdcall
    {
        __asm push This;
    }

    __asm//调用函数
    {
        call funcaddr; //call 1.向堆栈中压入下一行程序的地址;2.JMP到call的子程序地址处。
        mov  re,eax;
    }

    return re;
}

//////////////////////////////////////////////////////////////////////////////////////////////////
void test1()//演示c++成员函数指针的用法.
{
   class tt
   {
       public: void foo(int x){ printf("\n %d \n",x); }
   };

   typedef   void (tt::* FUNCTYPE)(int);


    FUNCTYPE ptr = &tt::foo;  //给一个成员函数指针赋值.

    tt a;
    (a.*ptr)(5);   //调用成员函数指针.

    tt *b = new tt;
    (b->*ptr)(6);  //调用成员函数指针.

    delete b;
//    DWORD dwFooAddrPtr= 0;
//    dwFooAddrPtr = (DWORD) &tt::foo;  /* Error C2440 */
//    dwFooAddrPtr = reinterpret_cast<DWORD> (&tt::foo); /* Error C2440 */
}

void test2()//示范如何取成员函数地址.
{
   class tt
   {
       public: void foo(int x){ printf("\n %d \n",x); }
   };

#if _MSC_VER >1200
    DWORD dwAddrPtr1;
    GetMemberFuncAddr_VC8(dwAddrPtr1,tt::foo);
    printf("\n test2 tt::foo %08x",dwAddrPtr1);
#endif

    DWORD dwAddrPtr2;
    GetMemberFuncAddr_VC6(dwAddrPtr2,&tt::foo);
    printf("\n test2 tt::foo %08x",dwAddrPtr2);
}

void test3()//示范如何调用成员函数地址.
{
    class tt 
    {
     public:

        void foo(int x,char c,char *s)//没有指定类型,默认是__thiscall.
        {
            printf("\n m_a=%d, %d,%c,%s\n",m_a,x,c,s);
        }

        void __stdcall foo2(int x,char c,char *s)//成员函数指定了__stdcall调用约定.
        {
            printf("\n m_a=%d, %d,%c,%s\n",m_a,x,c,s);
        }

        int m_a;
    };

    typedef  void (__stdcall *FUNCTYPE) (       int,char,char*);//定义对应的非成员函数指针类型,注意指定__stdcall.
    typedef  void (__stdcall *FUNCTYPE2)(void *,int,char,char*);//注意多了一个void *参数.

    tt abc;
    abc.m_a = 123;

    DWORD ptr;
    DWORD This = (DWORD)&abc;
 
    GetMemberFuncAddr_VC6(ptr,&tt::foo); //取成员函数地址.

    FUNCTYPE fnFooPtr  = (FUNCTYPE) ptr;//将函数地址转化为普通函数的指针. 

    __asm //准备this指针.
    {
        mov ecx, This;
    }

    fnFooPtr(5,'a',"7xyz"); //象普通函数一样调用成员函数的地址.


    GetMemberFuncAddr_VC6(ptr,&tt::foo2); //取成员函数地址.

    FUNCTYPE2 fnFooPtr2 = (FUNCTYPE2) ptr;//将函数地址转化为普通函数的指针. 

    fnFooPtr2(&abc,5,'a',"7xyz"); //象普通函数一样调用成员函数的地址,注意第一个参数是this指针.
}

void test4()//示范通过CallMemberFunc调用成员函数
{
    class tt 
    {
     public:

        void foo(int x,char c,char *s)//没有指定类型,默认是__thiscall.
        {
            printf("\n m_a=%d, %d,%c,%s\n",m_a,x,c,s);
        }

        void __stdcall foo2(int x,char c,char *s)//成员函数指定了__stdcall调用约定.
        {
            printf("\n m_a=%d, %d,%c,%s\n",m_a,x,c,s);
        }

        int m_a;
    };

    tt abc;
    abc.m_a = 123;

    DWORD ptr1,ptr2;

    GetMemberFuncAddr_VC6(ptr1,&tt::foo); //取成员函数地址.
    GetMemberFuncAddr_VC6(ptr2,&tt::foo2); //取成员函数地址.

    CallMemberFunc(0,ptr1,&abc,3,5,'a',"7xyz");//第一个参数0,表示采用__thiscall调用.
    CallMemberFunc(1,ptr2,&abc,3,5,'a',"7xyz");//第一个参数1,表示采用非__thiscall调用.  
}

void test5()//示范在继承情况下使用函数地址.
{
    class tt1
    {
    public:
                void foo1(){ printf("\n hi, i am in tt1::foo1\n");        }
        virtual void foo3(){ printf("\n hi, i am in tt1::foo3\n");    }
    };

    class tt2 : public tt1
    {
    public:
                void foo2(){ printf("\n hi, i am in tt2::foo2\n");  }
        virtual void foo3(){ printf("\n hi, i am in tt2::foo3\n");    }
    };


    DWORD tt1_foo3,tt2_foo1,tt2_foo2,tt2_foo3;

    GetMemberFuncAddr_VC6(tt1_foo3,&tt1::foo3);
    GetMemberFuncAddr_VC6(tt2_foo1,&tt2::foo1);
    GetMemberFuncAddr_VC6(tt2_foo2,&tt2::foo2);
    GetMemberFuncAddr_VC6(tt2_foo3,&tt2::foo3);

    tt1 x;
    tt2 y;

    CallMemberFunc(0,tt1_foo3,&x,0); // tt1::foo3
    CallMemberFunc(0,tt2_foo1,&x,0); // tt2::foo1 = tt1::foo1
    CallMemberFunc(0,tt2_foo2,&x,0); // tt2::foo2
    CallMemberFunc(0,tt2_foo3,&x,0); // tt2::foo3

    CallMemberFunc(0,tt1_foo3,&y,0); // tt1::foo3
    CallMemberFunc(0,tt2_foo1,&y,0); // tt2::foo1 = tt1::foo1
    CallMemberFunc(0,tt2_foo2,&y,0); // tt2::foo2
    CallMemberFunc(0,tt2_foo3,&y,0); // tt2::foo3
}

int main(int argc, char* argv[])
{
    test1();
    test2();
    test3();
    test4();
    test5();

   return 0;
}

http://download.csdn.net/download/tikycc2/1580557

posted @ 2015-12-16 05:20  findumars  Views(1718)  Comments(0Edit  收藏  举报