从函数结构成员获取函数地址

时间:2013-01-27 08:33:44

标签: c++ visual-studio assembly structure

我正在尝试获取隐藏在结构后面的函数地址。遗憾的是, void* 基本C ++转换不起作用,因此我使用了 C++ template

1。基本 void* C ++转换不适用于结构内部的函数,为什么?

void * lpfunction;
lpfunction = scanf; //OK
lpfunction = MessageBoxA; //OK

我做了一个简单的结构:

struct FOO{

    void PRINT(void){printf("bla bla bla");}

    void SETA(int){} //nothing you can see
    void SETB(int){} //nothing you can see

    int GETA(void){} //nothing you can see
    int GETB(void){} //nothing you can see
};
///////////////////////////////////////////
void *lpFunction = FOO::PRINT;

编译错误:

error C2440: 'initializing' : 
cannot convert from 'void (__thiscall FOO::*)(void)' to 'void *'

2。是否无法获得功能成员地址?

然后,我创建了一个模板函数,它可以将函数成员转换为地址。然后我将通过汇编调用它。它应该是这样的:

template <class F,void (F::*Function)()>  
void * GetFunctionAddress() {

    union ADDRESS  
    { 
        void (F::*func)();  
        void * lpdata;  
    }address_data;  

    address_data.func = Function;  
    return address_data.lpdata; //Address found!!!  

}  

以下是代码:

int main()
{
    void * address = GetFunctionAddress<FOO,&FOO::PRINT>();

    FOO number;
    number.PRINT(); //Template call
    void * lpdata = &number;

    __asm mov ecx, lpdata //Attach "number" structure address
    __asm call address //Call FOO::PRINT with assembly using __thiscall

printf("Done.\n");
system("pause");
return 0;
}

但是,我认为非常具体。它看起来像 LOCK - KEY ,我必须为每组参数类型创建一个新模板。

原创(好):

void PRINT(); //void FOO::PRINT();

修改一下:

void PRINT(int); //void FOO::PRINT(int);

编译器立即使用旧模板代码显示:

//void (F::*func)();
//address_data.func = Function;

error C2440: '=' : cannot convert from 
'void (__thiscall FOO::*)(int)' to 'void (__thiscall FOO::*)(void)'

为什么?它们只是地址。

69:       address_data.func = Function;
00420328  mov  dword ptr [ebp-4],offset @ILT+2940(FOO::PRINT) (00401b81)

...

EDIT3:我知道更好的解决方案:

void(NUMBER::*address_PRINT)(void) = FOO::PRINT;
int(NUMBER::*address_GETA)(void) = FOO::GETA;
int(NUMBER::*address_GETB)(void) = FOO::GETB;
void(NUMBER::*address_SETA)(int) = FOO::SETA;
void(NUMBER::*address_SETA)(int) = FOO::SETB;

它比模板好得多。顺便说一句,我想实现目标:

<special_definition> lpfunction;
lpfunction = FOO::PRINT; //OK
lpfunction = FOO::GETA; //OK
lpfunction = FOO::GETB; //OK
lpfunction = FOO::SETA; //OK
lpfunction = FOO::SETB; //OK

这可能吗?

3 个答案:

答案 0 :(得分:6)

指向成员函数的指针与指向全局函数或静态成员函数的指针完全不同。造成这种情况的原因有很多,但我不确定您对C ++的工作原理有多了解,所以我不确定哪些理由是有道理的。

我知道你在装配中尝试的东西根本不适用于一般情况。看起来你对成员函数和函数指针的目的有一个基本的误解。

问题是,你正在做一些你通常不会在C ++中做的事情。您通常不会在C ++中构建函数指针表,因为您将使用的那些东西是virtual函数的用途。

如果你决定使用这种方法,我建议你根本不使用C ++,只使用C语言。

为了证明这些指针类型完全不兼容,这里有一个程序:

#include <cstdio>

struct Foo {
   int a;
   int b;
   int addThem() { return a + b; }
};

struct Bar {
   int c;
   int d;
   int addThemAll() { return c + d; }
};

struct Qux : public Foo, public Bar {
   int e;
   int addAllTheThings() { return Foo::addThem() + Bar::addThemAll() + e; }
};

int addThemGlobal(Foo *foo)
{
   return foo->a + foo->b;
}

int main()
{
   int (Qux::*func)();

   func = &Bar::addThemAll;
   printf("sizeof(Foo::addThem) == %u\n", sizeof(&Foo::addThem));
   printf("sizeof(Bar::addThemAll) == %u\n", sizeof(&Bar::addThemAll));
   printf("sizeof(Qux::addAllTheThings) == %u\n", sizeof(&Qux::addAllTheThings));
   printf("sizeof(func) == %u\n", sizeof(func));
   printf("sizeof(addThemGlobal) == %u\n", sizeof(&addThemGlobal));
   printf("sizeof(void *) == %u\n", sizeof(void *));
   return 0;
}

在我的系统上,该程序产生以下结果:

$ /tmp/a.out 
sizeof(Foo::addThem) == 16
sizeof(Bar::addThemAll) == 16
sizeof(Qux::addAllTheThings) == 16
sizeof(func) == 16
sizeof(addThemGlobal) == 8
sizeof(void *) == 8

注意成员函数指针的长度是16个字节。它不适合void *。它不是正常意义上的指针。您的代码和union完全是偶然的。

这样做的原因是成员函数指针经常需要存储在其中的额外数据,这些数据与修复它传递的对象指针有关,以便对被调用的函数是正确的。在我的示例中,当在Bar::addThemAll对象上调用Qux时(由于继承而完全有效),需要调整指向Qux对象的指针以指向Bar调用函数之前的子对象。因此Qux::*成员函数必须在其中编码此调整。毕竟,说func = &Qux::addAllTheThings完全有效,如果调用该函数,则不需要指针调整。因此指针调整是函数指针值的一部分。

这只是一个例子。允许编译器以他们认为合适的任何方式实现成员函数指针(在某些约束内)。许多编译器(比如我正在使用的64位平台上的GNU C ++编译器)将以不允许任何成员函数指针被视为完全等同于普通函数指针的方式实现它们。

有办法解决这个问题。处理成员函数指针的瑞士军刀是C ++ 11或C ++ TR1中的::std::function模板。

一个例子:

 #include <functional>

 // .... inside main
    ::std::function<int(Qux *)> funcob = func;

funcob可以指向任何可以像函数一样调用的东西,并且需要Qux *。成员函数,全局函数,静态成员函数,仿函数...... funcob可以指出它。

该示例仅适用于C ++ 11编译器。但是如果你的编译器是合理的最新版本,但仍然不是C ++ 11编译器,那么这可能会起作用:

 #include <tr1/functional>

 // .... inside main
    ::std::tr1::function<int(Qux *)> funcob = func;

如果情况变得更糟,你可以使用Boost库,这是整个概念的来源。

但我会重新考虑你的设计。我怀疑你会有更好的继承层次和使用virtual函数,而不是你现在正在做的任何事情。使用解释器,我将有一个顶级抽象'表达式'类,它是可以评估的任何东西的抽象类。我会给它一个虚拟的evaluate方法。然后,您可以派生不同语法元素的类,例如加法表达式变量或常量。他们每个人都会为其特定情况重载评估方法。然后你可以建立表达式树。

虽然不知道细节,但这只是对你的设计的一个模糊的建议。

答案 1 :(得分:2)

这是一个干净的解决方案。通过模板将您的成员函数包装到静态成员函数中。然后你可以把它转换成你想要的任何指针:

template<class F, void (F::*funct)()>
struct Helper: public T {
  static void static_f(F *obj) {
    ((*obj).*funct)();
  };
};

struct T {
  void f() {
  }
};

int main() {
  void (*ptr)(T*);
  ptr = &(Helper<T,&T::f>::static_f);
}

答案 2 :(得分:-1)

您似乎需要将指向成员函数的指针转换为void *。我假设您想将该指针作为“用户数据”提供给某个库函数,然后您将返回指针并希望在某个给定对象上使用它。

如果是这种情况,reinterpret_cast<void *>(...)可能是正确的...我假设接收指针的库没有使用它。