【问题标题】:Class method as winAPI callback类方法作为 winAPI 回调
【发布时间】:2010-09-16 09:40:11
【问题描述】:

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

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

提前致谢:)。

【问题讨论】:

    标签: c++ winapi


    【解决方案1】:

    您不能将非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);
      }
    };
    

    【讨论】:

    • 这有点令人困惑,但我理解这个概念(我认为)。
    • 我现在明白了。我认为它不会编译,因为 callback_ 是在 call_some_func() 中使用后声明的;
    • @Alexander:内联成员函数的编译就像它们是在类定义之后立即定义的一样。所以这不是问题。
    【解决方案2】:

    如果您使用 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。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        您可以将类的适当静态成员函数设置为来自 Windows API 的回调函数。

        【讨论】:

          猜你喜欢
          • 2013-01-17
          • 2012-11-12
          • 1970-01-01
          • 2021-07-31
          • 1970-01-01
          • 2017-01-08
          • 1970-01-01
          • 2017-07-15
          • 2011-04-19
          相关资源
          最近更新 更多