将winAPI消息回调函数设置为类的方法是否可行。如果是这样,这将如何最好地实施?我想知道它是否有可能。
很抱歉这个简短的问题,希望您能够提供有用的回复。
提前致谢:)。
答案 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是虚拟的,则此断言可能会失败。 要禁止多重继承,请使用__single_inheritance keyword:
struct __single_inheritance Foo { /* ....*/ };
其次,您应该使用
将&amp; Foo :: someCallback强制转换为callback_tunion
{
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中将类的相应静态成员函数设置为回调函数。