作为winAPI回调的类方法

时间:2010-09-16 09:40:11

标签: c++ winapi

将winAPI消息回调函数设置为类的方法是否可行。如果是这样,这将如何最好地实施?我想知道它是否有可能。

很抱歉这个简短的问题,希望您能够提供有用的回复。

提前致谢:)。

4 个答案:

答案 0 :(得分:7)

您不能将非static成员函数用于C回调。

但是,通常C回调都有一个用户数据指针,该指针被路由到回调。在static成员函数的帮助下,可以探索这些来做你想做的事情:

// Beware, brain-compiled code ahead!

typedef void (*callback)(int blah, void* user_data);

void some_func(callback cb, void* user_data);

class my_class {
public:
  // ...
  void call_some_func()
  {
     some_func(&callback_,this);
  }
private:
  void callback(int blah)
  {
    std::cout << blah << '\n';
  }
  static void callback_(int blah, void* user_data)
  {
    my_class* that = static_cast<my_class*>(user_data);
    that->callback(blah);
  }
};

答案 1 :(得分:2)

如果使用MSVC,并且目标为x86-32(不是x86-64),则可以对成员函数使用__stdcall约定。 (__ cdecl也有效)

使用__stdcall ,此将作为第一个参数传递,因此您可以编写

typedef void(__stdcall *callback_t)(void* arg);
void set_callback(callback_t callback, void* callbackParameter);

struct Foo
{
   int x;
   void __stdcall someCallback() { this->x = 1; }
};

Foo foo;
set_callback((callback_t)&Foo::someCallback, this);

无法正常工作:你无法通过标准直接将成员函数指针转换为指针。您应该使用解决方法来实现此功能:

首先,断言&amp; Foo :: someCallback的大小与void *

相同
static_assert(sizeof(&Foo::someCallback) == sizeof(void*), "");
// or assert()

如果可能存在多个继承,并且someCallback是虚拟的,则此断言可能会失败。 要禁止多重继承,请使用__si​​ngle_inheritance keyword:

struct __single_inheritance Foo { /* ....*/ };

其次,您应该使用

将&amp; Foo :: someCallback强制转换为callback_t
union
{
    void(__stdcall Foo::*src)();
    callback_t dst;    
} u = {&Foo::someCallback};
set_callback(u.dst, this);

void(__stdcall Foo::*temp)() = &Foo::someCallback;
set_callback(*(callback_t*)&temp, this);

仅当您可以将作为第一个参数传递给回调时,它才有效。

如果你不能,你可以尝试在程序集中动态生成回调存根:)分配一个可执行内存并写在那里

B9 XX XX XX XX mov ecx, <this>
68 XX XX XX XX push <function member address>
C3 ret

它将是回调存根,它将__stdcall转换为__thiscall。

答案 2 :(得分:1)

如果它具有正确的签名和正确的调用约定,则可以将静态类方法作为各种Win32 API的回调函数传递。您不能使用非静态方法,因为它们需要一个更多的隐式参数(this指针),Win32 APi无法为它们提供这些参数。

答案 3 :(得分:0)

您可以从Windows API中将类的相应静态成员函数设置为回调函数。