2010-09-16 7 views
3

Est-il possible de définir la fonction de rappel de message winAPI en tant que méthode d'une classe. Si oui, comment cela serait-il mieux mis en œuvre? Je me demande si c'est même possible.Méthode de classe en tant que rappel winAPI

Désolé pour la question courte, j'espère que vous serez en mesure de fournir des réponses utiles.

Merci d'avance :).

Répondre

7

Vous ne pouvez pas utiliser de fonctions membres non static pour les rappels C. Toutefois, les rappels C ont généralement un pointeur de données utilisateur qui est routé vers le rappel. Cela peut être exploré à faire ce que vous voulez avec l'aide d'une des fonctions membres 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); 
    } 
}; 
+0

C'est un peu déroutant mais je comprends le concept (je pense). –

+0

Je l'ai maintenant. Je ne pense pas qu'il compilerait car callback_ a été déclaré après son utilisation dans call_some_func(); –

+0

@Alexander: Les fonctions membres en ligne sont compilées comme si elles étaient définies immédiatement après la définition de leur classe. Donc, ce n'est pas un problème. – sbi

1

Pourvu qu'il a la bonne signature et le droit convention d'appel, vous pouvez passer des méthodes statiques de classe comme des fonctions de rappel pour divers API Win32. Vous ne pouvez pas utiliser de méthodes non statiques, car elles attendent un autre paramètre implicite (le pointeur this), que l'API Win32 ne peut pas leur donner.

0

Vous pouvez définir une fonction membre statique appropriée d'une classe en tant que fonction de rappel à partir d'API Windows.

2

Si vous utilisez MSVC et ciblez x86-32 (pas x86-64), vous pouvez utiliser la convention __stdcall pour la fonction de membre. (__cdecl fonctionne aussi)

Avec __stdcall ce sera passé comme premier paramètre, de sorte que vous pouvez écrire

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; } 
}; 

mais

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

ne fonctionnera pas: vous ne pouvez pas directement jeté Pointeur de fonction membre pointeur par standard. Vous devez utiliser une solution de contournement pour faire ce travail:

D'abord, affirmer que & Foo :: someCallback a la même taille que void *

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

Cette assertion peut échouer, s'il peut y avoir l'héritage multiple, et someCallback est virtuel. Pour désactiver l'héritage multiple, utilisez mot-clé __single_inheritance:

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

Deuxièmement, vous devriez jeter & Foo :: someCallback à callback_t en utilisant

union 
{ 
    void(__stdcall Foo::*src)(); 
    callback_t dst;  
} u = {&Foo::someCallback}; 
set_callback(u.dst, this); 

ou

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

Il ne fonctionne que si vous pouvez passer ce au rappel en tant que premier paramètre.

Si vous ne pouvez pas, vous pouvez essayer de générer dynamiquement des talons de rappel dans l'assemblage :) Allouer une mémoire exécutable et il écrire

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

Il sera talon de rappel, qui convertira __stdcall à __thiscall.

Questions connexes