COM : Interface IUnknown

 

Télécharger le code source

Retour au COM :
La dernière fois, nous avons vu comment exporter une classe d'une dll. Mais COM resout d'autres problème, par exemple celui de la durée de vie des objet que nous allons aborder aujourd'hui.

Le problème :
Considérons un pointeur vers une instance de classe, de triangle par exemple. Ce pointeur, votre moteur 3d en gardera sans doute une référence pour pouvoir dessiner ce triangle, votre moteur physique pour gérer les collisions avec. Quand il s'agira de detruire cette instance, qui s'en chargera, le moteur 3d ou physique.
D'accord dans ce cas simple on peut se débrouiller. Mais la question peut déjà se poser et on peut vite faire des erreurs en voulant deleter un pointeur 2 fois.

La solution :
Elle passe par l'interface IUnknown définit comme il suit:

interface IUnknown 
{
    virtual HRESULT	QueryInterface( REFIID riid, void** ppvObject) =0;
    virtual ULONG	AddRef( void ) =0;
    virtual ULONG	Release( void ) =0;
}
Si vous rechercher un peu dans unknwn.h vous verrez que la définition est sensiblement plus longue et plus complexe. Par souci de simplicité je l'ai mise sous une forme plus humaine. Je rapelle que interface est un synonyme de struct qui, sous VC++, est une classe qui par défaut est public au lieu de private pour class.

Mode d'emploi de la solution :
Alors à quoi peut bien servir cette interface. Tout, d'abord sachez que tout interface COM dérive de cette interface ci. Ne faites pas trop attention à QueryInterface pour l'instant, nous y reviendrons plus tard. Vous l'aurez compris, ce qui nous interresse, c'est les méthodes AddRef et Release, le fonctionnement est très simple.

Compteur de référence:
La démarche à suivre est la suivante, dès que vous ferez pointer un pointeur vers une instance, il faudra appeler tout de suite après la méthode AddRef. De même, juste avant de rediriger ou de détruire le-dit pointeur, il faut appeler Release. C'est relativement simple.

Implémentation :
Considérons la classe CUnknown qui dérive de IUnknown:

class CUnknown: public IUnknown
{
    public:
    // le constructeur mets m_cRef à 0
    CUnknown() : m_cRef(0) {}

    // IUnknown
    virtual HRESULT	QueryInterface( REFIID riid, void** ppvObject) { return E_NOINTERFACE; }
    virtual ULONG	AddRef( void );
    virtual ULONG	Release( void );

    private:
    ULONG		m_cRef;
}

// Implémentation
ULONG CUnknown::AddRef( void )
{
    return m_cRef++;
}
ULONG CUnknown::Release(void)
{
    if ( --m_cRef == 0){
	delete this ;
	return 0 ;
    }
    return m_cRef ;
}
Commentons ce bout de code, on commence par faire un constructeur qui initialise le membre m_cRef à 0. Ce membre, par alieurs, sera notre compteur de référence, on commence donc avec 0 références. Ensuite QueryInterface qui retourne la valeur E_NOINTERFACE pour dire que l'appel à échouer, mais nous verrons ca en profondeur un autre jour.
AddRef et Release incrémente et décrémente respectivement le membre m_cRef. De plus, si après la décrementation, le compteur de références tombe à 0, on détruit l'instance de l'objet, en effet si plus aucun pointeur ne pointe sur l'interface, c'est qu'elle ne sert plus.

Exemple fournit :
Dans le projet join au tutorial, j'ai tout simplement utilisé l'exemple bateau d'un classe qui dérive uniquement de IUnknown.
Note: L'exemple utilise la macro STDMETHODCALLTYPE, c'est juste une méthode d'appel de fonction nécessaire car IUnknown l'utilise.

Au prochain numéro :
La prochaine halte dans le monde de COM n'est pas encore définie. J'hésite entre vous montrer l'utilité d'utiliser des interfaces virtuelles pures pour créer par exemple un moteur qui utilise de manière transparente DirectX ou OpenGL. Ou bien expliquer la structure de composant à interfaces multiples. N'hésitez pas a me maillez si vous préférez l'un ou l'autre des tutorials ou bien si vous avez une ou des question(s).