Метод класса как обратный вызов winAPI - PullRequest
3 голосов
/ 16 сентября 2010

Возможно ли установить функцию обратного вызова сообщения winAPI как метод класса. Если да, то как это лучше всего реализовать? Интересно, возможно ли это вообще?

Извините за короткий вопрос, надеюсь, вы сможете дать полезные ответы.

Заранее спасибо:).

Ответы [ 4 ]

7 голосов
/ 16 сентября 2010

Вы не можете использовать не-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);
  }
};
2 голосов
/ 16 сентября 2010

Если вы используете 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);

Не будет работать: вы не можете напрямую привести указатель на функцию-член к стандартному указателю.Чтобы это сработало, вы должны использовать обходной путь:

Сначала убедитесь, что & 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.

1 голос
/ 16 сентября 2010

При условии, что он имеет правильную подпись и правильное соглашение о вызовах, вы можете передавать статические методы класса в качестве функций обратного вызова для различных Win32 API. Нельзя использовать нестатические методы, так как они ожидают еще один неявный параметр (указатель this), который Win32 APi не может им дать.

0 голосов
/ 16 сентября 2010

Вы можете установить соответствующую статическую функцию-член класса в качестве функции обратного вызова из API Windows.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...