【发布时间】:2010-09-16 09:40:11
【问题描述】:
将winAPI消息回调函数设置为类的方法是否可行。如果是这样,这将如何最好地实施?我想知道这是否可能。
很抱歉这个简短的问题,希望您能提供有用的答复。
提前致谢:)。
【问题讨论】:
将winAPI消息回调函数设置为类的方法是否可行。如果是这样,这将如何最好地实施?我想知道这是否可能。
很抱歉这个简短的问题,希望您能提供有用的答复。
提前致谢:)。
【问题讨论】:
您不能将非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);
}
};
【讨论】:
如果您使用 MSVC,并且以 x86-32(不是 x86-64)为目标,则可以对成员函数使用 __stdcall 约定。 (__cdecl 也可以)
使用 __stdcall this 将作为第一个参数传递,因此您可以编写
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);
将不起作用:您不能按标准将成员函数指针直接转换为指针。您应该使用一种解决方法来使其正常工作:
首先,断言 &Foo::someCallback 的大小与 void* 相同
static_assert(sizeof(&Foo::someCallback) == sizeof(void*), "");
// or assert()
这个断言可能会失败,如果可能有多重继承,并且 someCallback 是虚拟的。 要禁止多重继承,请使用 __single_inheritance 关键字:
struct __single_inheritance Foo { /* ....*/ };
其次,你应该将 &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);
仅当您可以将 this 作为第一个参数传递给回调时,它才有效。
如果不能,可以尝试在程序集中动态生成回调存根 :) 分配一个可执行内存并写入那里
B9 XX XX XX XX mov ecx, <this>
68 XX XX XX XX push <function member address>
C3 ret
它将是回调存根,它将 __stdcall 转换为 __thiscall。
【讨论】:
只要它具有正确的签名和正确的调用约定,您就可以将静态类方法作为回调函数传递给各种 Win32 API。您不能使用非静态方法,因为它们需要一个 Win32 APi 无法提供给它们的隐式参数(this 指针)。
【讨论】:
您可以将类的适当静态成员函数设置为来自 Windows API 的回调函数。
【讨论】: