Partager via


TN038 : Implémentation MFC/OLE IUnknown

Remarque

La note technique suivante n’a pas été mise à jour depuis sa première inclusion dans la documentation en ligne. Par conséquent, certaines procédures et rubriques peuvent être obsolètes ou incorrectes. Pour obtenir les informations les plus récentes, il est recommandé de rechercher la rubrique intéressante dans l’index de documentation en ligne.

Au cœur d’OLE 2 est le « modèle objet de composant OLE » ou COM. COM définit une norme pour la façon dont les objets de coopération communiquent entre eux. Cela inclut les détails de l’apparence d’un « objet », notamment la façon dont les méthodes sont distribuées sur un objet. COM définit également une classe de base à partir de laquelle toutes les classes compatibles COM sont dérivées. Cette classe de base est IUnknown. Bien que l’interface IUnknown soit appelée classe C++, COM n’est pas spécifique à un langage , il peut être implémenté en C, PASCAL ou tout autre langage qui peut prendre en charge la disposition binaire d’un objet COM.

OLE fait référence à toutes les classes dérivées d’IUnknown comme « interfaces ». Il s’agit d’une distinction importante, car une « interface » telle que IUnknown ne porte pas d’implémentation. Il définit simplement le protocole par lequel les objets communiquent, et non les spécificités de ce que ces implémentations font. Cela est raisonnable pour un système qui permet une flexibilité maximale. Il s’agit du travail de MFC pour implémenter un comportement par défaut pour les programmes MFC/C++.

Pour comprendre l’implémentation de MFC de IUnknown , vous devez d’abord comprendre ce qu’est cette interface. Une version simplifiée d’IUnknown est définie ci-dessous :

class IUnknown
{
public:
    virtual HRESULT QueryInterface(REFIID iid, void** ppvObj) = 0;
    virtual ULONG AddRef() = 0;
    virtual ULONG Release() = 0;
};

Remarque

Certains détails de convention d’appel nécessaires, tels que __stdcall ceux-ci, sont laissés à l’écart pour cette illustration.

Les fonctions membres AddRef et Release contrôlent la gestion de la mémoire de l’objet. COM utilise un schéma de comptage de références pour effectuer le suivi des objets. Un objet n’est jamais référencé directement comme vous le feriez en C++. Au lieu de cela, les objets COM sont toujours référencés via un pointeur. Pour libérer l’objet lorsque le propriétaire l’utilise, le membre Release de l’objet est appelé (par opposition à l’utilisation de la suppression d’opérateur, comme cela serait fait pour un objet C++ traditionnel). Le mécanisme de comptage de références permet de gérer plusieurs références à un seul objet. Une implémentation de AddRef et Release conserve un nombre de références sur l’objet : l’objet n’est pas supprimé tant que son nombre de références n’atteint pas zéro.

AddRef et Release sont assez simples du point de vue de l’implémentation. Voici une implémentation triviale :

ULONG CMyObj::AddRef()
{
    return ++m_dwRef;
}

ULONG CMyObj::Release()
{
    if (--m_dwRef == 0)
    {
        delete this;
        return 0;
    }
    return m_dwRef;
}

La fonction membre QueryInterface est un peu plus intéressante. Il n’est pas très intéressant d’avoir un objet dont seules les fonctions membres sont AddRef et Release , il serait agréable de dire à l’objet de faire plus de choses que IUnknown fournit. C’est là que QueryInterface est utile. Il vous permet d’obtenir une autre « interface » sur le même objet. Ces interfaces sont généralement dérivées d’IUnknown et ajoutent des fonctionnalités supplémentaires en ajoutant de nouvelles fonctions membres. Les interfaces COM n’ont jamais de variables membres déclarées dans l’interface, et toutes les fonctions membres sont déclarées en tant que pure-virtual. Par exemple,

class IPrintInterface : public IUnknown
{
public:
    virtual void PrintObject() = 0;
};

Pour obtenir un IPrintInterface si vous n’avez qu’un IUnknown, appelez QueryInterface à l’aide IIDIPrintInterfacedu fichier . Il IID s’agit d’un nombre 128 bits qui identifie de manière unique l’interface. Il existe une IID interface que vous ou OLE définissez. Si pUnk est un pointeur vers un objet IUnknown , le code à extraire d’un IPrintInterface peut être :

IPrintInterface* pPrint = NULL;
if (pUnk->QueryInterface(IID_IPrintInterface, (void**)&pPrint) == NOERROR)
{
    pPrint->PrintObject();
    pPrint->Release();
    // release pointer obtained via QueryInterface
}

Cela semble assez facile, mais comment implémenter un objet prenant en charge à la fois l’interface IPrintInterface et IUnknown Dans ce cas, il est simple, car IPrintInterface est dérivé directement de IUnknown — en implémentant IPrintInterface, IUnknown est automatiquement pris en charge. Par exemple:

class CPrintObj : public CPrintInterface
{
    virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
    virtual ULONG AddRef();
    virtual ULONG Release();
    virtual void PrintObject();
};

Les implémentations d’AddRef et release seraient exactement les mêmes que celles implémentées ci-dessus. CPrintObj::QueryInterface ressemblerait à ceci :

HRESULT CPrintObj::QueryInterface(REFIID iid, void FAR* FAR* ppvObj)
{
    if (iid == IID_IUnknown || iid == IID_IPrintInterface)
    {
        *ppvObj = this;
        AddRef();
        return NOERROR;
    }
    return E_NOINTERFACE;
}

Comme vous pouvez le voir, si l’identificateur d’interface (IID) est reconnu, un pointeur est retourné à votre objet ; sinon, une erreur se produit. Notez également qu’une requête QueryInterface réussie entraîne un AddRef implicite. Bien sûr, vous devrez également implémenter CEditObj ::P rint. C’est simple, car IPrintInterface a été directement dérivé de l’interface IUnknown . Toutefois, si vous souhaitez prendre en charge deux interfaces différentes, toutes deux dérivées d’IUnknown, tenez compte des éléments suivants :

class IEditInterface : public IUnkown
{
public:
    virtual void EditObject() = 0;
};

Bien qu’il existe plusieurs façons d’implémenter une classe prenant en charge IEditInterface et IPrintInterface, notamment l’utilisation de l’héritage multiple C++, cette note se concentre sur l’utilisation de classes imbriquées pour implémenter cette fonctionnalité.

class CEditPrintObj
{
public:
    CEditPrintObj();

    HRESULT QueryInterface(REFIID iid, void**);
    ULONG AddRef();
    ULONG Release();
    DWORD m_dwRef;

    class CPrintObj : public IPrintInterface
    {
    public:
        CEditPrintObj* m_pParent;
        virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
        virtual ULONG AddRef();
        virtual ULONG Release();
    } m_printObj;

    class CEditObj : public IEditInterface
    {
    public:
        CEditPrintObj* m_pParent;
        virtual ULONG QueryInterface(REFIID iid, void** ppvObj);
        virtual ULONG AddRef();
        virtual ULONG Release();
    } m_editObj;
};

L’ensemble de l’implémentation est inclus ci-dessous :

CEditPrintObj::CEditPrintObj()
{
    m_editObj.m_pParent = this;
    m_printObj.m_pParent = this;
}

ULONG CEditPrintObj::AddRef()
{
    return ++m_dwRef;
}

CEditPrintObj::Release()
{
    if (--m_dwRef == 0)
    {
        delete this;
        return 0;
    }
    return m_dwRef;
}

HRESULT CEditPrintObj::QueryInterface(REFIID iid, void** ppvObj)
{
    if (iid == IID_IUnknown || iid == IID_IPrintInterface)
    {
        *ppvObj = &m_printObj;
        AddRef();
        return NOERROR;
    }
    else if (iid == IID_IEditInterface)
    {
        *ppvObj = &m_editObj;
        AddRef();
        return NOERROR;
    }
    return E_NOINTERFACE;
}

ULONG CEditPrintObj::CEditObj::AddRef()
{
    return m_pParent->AddRef();
}

ULONG CEditPrintObj::CEditObj::Release()
{
    return m_pParent->Release();
}

HRESULT CEditPrintObj::CEditObj::QueryInterface(REFIID iid, void** ppvObj)
{
    return m_pParent->QueryInterface(iid, ppvObj);
}

ULONG CEditPrintObj::CPrintObj::AddRef()
{
    return m_pParent->AddRef();
}

ULONG CEditPrintObj::CPrintObj::Release()
{
    return m_pParent->Release();
}

HRESULT CEditPrintObj::CPrintObj::QueryInterface(REFIID iid, void** ppvObj)
{
    return m_pParent->QueryInterface(iid, ppvObj);
}

Notez que la plupart de l’implémentation IUnknown est placée dans la classe CEditPrintObj plutôt que de dupliquer le code dans CEditPrintObj ::CEditObj et CEditPrintObj ::CPrintObj. Cela réduit la quantité de code et évite les bogues. Le point clé ici est que de l’interface IUnknown, il est possible d’appeler QueryInterface pour récupérer n’importe quelle interface que l’objet peut prendre en charge, et de chacune de ces interfaces, il est possible d’effectuer la même opération. Cela signifie que toutes les fonctions QueryInterface disponibles à partir de chaque interface doivent se comporter exactement de la même façon. Pour que ces objets incorporés appellent l’implémentation dans l'« objet externe », un pointeur arrière est utilisé (m_pParent). Le pointeur m_pParent est initialisé pendant le constructeur CEditPrintObj. Ensuite, vous implémentez CEditPrintObj ::CPrintObj ::P rintObject et CEditPrintObj ::CEditObj ::EditObject ainsi. Un peu de code a été ajouté pour ajouter une fonctionnalité , la possibilité de modifier l’objet. Heureusement, il est très rare que les interfaces n’aient qu’une seule fonction membre (bien qu’elle se produise) et, dans ce cas, EditObject et PrintObject sont généralement combinés en une seule interface.

C’est beaucoup d’explication et beaucoup de code pour un scénario si simple. Les classes MFC/OLE offrent une alternative plus simple. L’implémentation MFC utilise une technique similaire à la façon dont les messages Windows sont encapsulés avec Les cartes de messages. Cette fonctionnalité est appelée Cartes d’interface et est abordée dans la section suivante.

Cartes d’interface MFC

MFC/OLE inclut une implémentation de « Cartes d’interface » similaire à « Cartes de messages » de MFC et « Dispatch Maps » dans le concept et l’exécution. Les principales fonctionnalités des cartes d’interface MFC sont les suivantes :

  • Implémentation standard d’IUnknown, intégrée à la CCmdTarget classe.

  • Maintenance du nombre de références, modifiée par AddRef et Release

  • Implémentation pilotée par les données de QueryInterface

En outre, les cartes d’interface prennent en charge les fonctionnalités avancées suivantes :

  • Prise en charge de la création d’objets COM aggregatables

  • Prise en charge de l’utilisation d’objets d’agrégation dans l’implémentation d’un objet COM

  • L’implémentation est pouvant être connectée et extensible

Pour plus d’informations sur l’agrégation, consultez la rubrique Agrégation .

La prise en charge de la carte d’interface MFC est enracinée dans la CCmdTarget classe. CCmdTarget Nombre de références « has-a » ainsi que toutes les fonctions membres associées à l’implémentation IUnknown (le nombre de références par exemple est dans CCmdTarget). Pour créer une classe prenant en charge OLE COM, vous dérivez une classe de CCmdTarget différentes macros et utilisez diverses macros, ainsi que des fonctions membres de l’implémentation des CCmdTarget interfaces souhaitées. L’implémentation de MFC utilise des classes imbriquées pour définir chaque implémentation d’interface comme l’exemple ci-dessus. Cela est facilité avec une implémentation standard d’IUnknown ainsi qu’un certain nombre de macros qui éliminent certains du code répétitif.

Principes de base de la carte d’interface

Pour implémenter une classe à l’aide des cartes d’interface de MFC

  1. Dériver une classe directement ou indirectement de CCmdTarget.

  2. Utilisez la DECLARE_INTERFACE_MAP fonction dans la définition de classe dérivée.

  3. Pour chaque interface que vous souhaitez prendre en charge, utilisez les macros BEGIN_INTERFACE_PART et END_INTERFACE_PART dans la définition de classe.

  4. Dans le fichier d’implémentation, utilisez les macros BEGIN_INTERFACE_MAP et END_INTERFACE_MAP pour définir la carte d’interface de la classe.

  5. Pour chaque IID pris en charge, utilisez la macro INTERFACE_PART entre les macros BEGIN_INTERFACE_MAP et END_INTERFACE_MAP pour mapper ce IID à une « partie » spécifique de votre classe.

  6. Implémentez chacune des classes imbriquées qui représentent les interfaces que vous prenez en charge.

  7. Utilisez la macro METHOD_PROLOGUE pour accéder à l’objet parent dérivé CCmdTarget.

  8. AddRef, Release et QueryInterface peuvent déléguer à l’implémentation CCmdTarget de ces fonctions (ExternalAddRef, ExternalReleaseet ExternalQueryInterface).

L’exemple CPrintEditObj ci-dessus peut être implémenté comme suit :

class CPrintEditObj : public CCmdTarget
{
public:
    // member data and member functions for CPrintEditObj go here

// Interface Maps
protected:
    DECLARE_INTERFACE_MAP()

    BEGIN_INTERFACE_PART(EditObj, IEditInterface)
        STDMETHOD_(void, EditObject)();
    END_INTERFACE_PART(EditObj)

    BEGIN_INTERFACE_PART(PrintObj, IPrintInterface)
        STDMETHOD_(void, PrintObject)();
    END_INTERFACE_PART(PrintObj)
};

La déclaration ci-dessus crée une classe dérivée de CCmdTarget. La macro DECLARE_INTERFACE_MAP indique au framework que cette classe aura une carte d’interface personnalisée. En outre, les macros BEGIN_INTERFACE_PART et END_INTERFACE_PART définissent des classes imbriquées, dans ce cas avec les noms CEditObj et CPrintObj (le X est utilisé uniquement pour différencier les classes imbriquées des classes globales qui commencent par « C » et les classes d’interface qui commencent par « I »). Deux membres imbriqués de ces classes sont créés : m_CEditObj et m_CPrintObj, respectivement. Les macros déclarent automatiquement les fonctions AddRef, Release et QueryInterface ; par conséquent, vous déclarez uniquement les fonctions spécifiques à cette interface : EditObject et PrintObject (la macro OLE STDMETHOD est utilisée afin que _stdcall et les mots clés virtuels soient fournis en fonction de la plateforme cible).

Pour implémenter le mappage d’interface pour cette classe :

BEGIN_INTERFACE_MAP(CPrintEditObj, CCmdTarget)
    INTERFACE_PART(CPrintEditObj, IID_IPrintInterface, PrintObj)
    INTERFACE_PART(CPrintEditObj, IID_IEditInterface, EditObj)
END_INTERFACE_MAP()

Cela connecte les IID_IPrintInterface IID avec m_CPrintObj et IID_IEditInterface avec m_CEditObj respectivement. L’implémentation CCmdTarget de QueryInterface (CCmdTarget::ExternalQueryInterface) utilise cette carte pour renvoyer des pointeurs vers m_CPrintObj et m_CEditObj lorsque demandé. Il n’est pas nécessaire d’inclure une entrée pour IID_IUnknown; l’infrastructure utilise la première interface de la carte (dans ce cas, m_CPrintObj) quand elle IID_IUnknown est demandée.

Même si la macro BEGIN_INTERFACE_PART a automatiquement déclaré les fonctions AddRef, Release et QueryInterface pour vous, vous devez toujours les implémenter :

ULONG FAR EXPORT CEditPrintObj::XEditObj::AddRef()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return pThis->ExternalAddRef();
}

ULONG FAR EXPORT CEditPrintObj::XEditObj::Release()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return pThis->ExternalRelease();
}

HRESULT FAR EXPORT CEditPrintObj::XEditObj::QueryInterface(
    REFIID iid,
    void FAR* FAR* ppvObj)
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}

void FAR EXPORT CEditPrintObj::XEditObj::EditObject()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    // code to "Edit" the object, whatever that means...
}

L’implémentation de CEditPrintObj ::CPrintObj serait similaire aux définitions ci-dessus pour CEditPrintObj ::CEditObj. Bien qu’il soit possible de créer une macro qui pourrait être utilisée pour générer automatiquement ces fonctions (mais plus tôt dans le développement MFC/OLE, cela était le cas), il devient difficile de définir des points d’arrêt lorsqu’une macro génère plusieurs lignes de code. Pour cette raison, ce code est développé manuellement.

En utilisant l’implémentation de l’infrastructure des mappages de messages, il existe plusieurs choses qui n’ont pas été nécessaires pour effectuer les opérations suivantes :

  • Implémenter QueryInterface

  • Implémenter AddRef et la mise en production

  • Déclarer l’une de ces méthodes intégrées sur les deux interfaces

En outre, l’infrastructure utilise des mappages de messages en interne. Cela vous permet de dériver d’une classe d’infrastructure, par exemple COleServerDoc, qui prend déjà en charge certaines interfaces et fournit des remplacements ou des ajouts aux interfaces fournies par l’infrastructure. Pour ce faire, l’infrastructure prend entièrement en charge l’héritage d’une carte d’interface à partir d’une classe de base. C’est la raison pour laquelle BEGIN_INTERFACE_MAP prend comme deuxième paramètre le nom de la classe de base.

Remarque

Il n’est généralement pas possible de réutiliser l’implémentation des implémentations intégrées de MFC des interfaces OLE en héritent simplement de la spécialisation incorporée de cette interface à partir de la version MFC. Cela n’est pas possible, car l’utilisation de la macro METHOD_PROLOGUE pour obtenir l’accès à l’objet CCmdTargetconteneur dérivé implique un décalage fixe de l’objet incorporé à partir de l’objet CCmdTargetdérivé -. Cela signifie, par exemple, que vous ne pouvez pas dériver un XMyAdviseSink incorporé de l’implémentation de MFC, COleClientItem::XAdviseSinkcar XAdviseSink s’appuie sur un décalage spécifique du haut de l’objet COleClientItem .

Remarque

Toutefois, vous pouvez déléguer à l’implémentation MFC pour toutes les fonctions que vous souhaitez avoir le comportement par défaut de MFC. Cela s’effectue dans l’implémentation MFC de IOleInPlaceFrame (XOleInPlaceFrame) dans la COleFrameHook classe (elle délègue à m_xOleInPlaceUIWindow pour de nombreuses fonctions). Cette conception a été choisie pour réduire la taille du runtime des objets qui implémentent de nombreuses interfaces ; il élimine la nécessité d’un pointeur arrière (par exemple, la façon dont m_pParent a été utilisé dans la section précédente).

Agrégation et cartes d’interface

Outre la prise en charge des objets COM autonomes, MFC prend également en charge l’agrégation. L’agrégation elle-même est trop complexe pour discuter ici ; reportez-vous à la rubrique Agrégation pour plus d’informations sur l’agrégation. Cette note décrit simplement la prise en charge de l’agrégation intégrée au framework et aux cartes d’interface.

Il existe deux façons d’utiliser l’agrégation : (1) à l’aide d’un objet COM prenant en charge l’agrégation et (2) implémentant un objet pouvant être agrégé par un autre. Ces fonctionnalités peuvent être appelées « utilisation d’un objet d’agrégation » et « création d’un objet aggregatable ». MFC prend en charge les deux.

Utilisation d’un objet d’agrégation

Pour utiliser un objet d’agrégation, il doit y avoir un moyen de lier l’agrégat au mécanisme QueryInterface. En d’autres termes, l’objet d’agrégation doit se comporter comme s’il s’agit d’une partie native de votre objet. Ainsi, comment cela est lié au mécanisme de carte d’interface de MFC en plus de la macro INTERFACE_PART, où un objet imbriqué est mappé à un IID, vous pouvez également déclarer un objet d’agrégation dans le cadre de votre CCmdTarget classe dérivée. Pour ce faire, la macro INTERFACE_AGGREGATE est utilisée. Cela vous permet de spécifier une variable membre (qui doit être un pointeur vers une classe IUnknown ou dérivée), qui doit être intégrée au mécanisme de mappage d’interface. Si le pointeur n’est pas NULL lorsqu’il CCmdTarget::ExternalQueryInterface est appelé, l’infrastructure appelle automatiquement la fonction membre QueryInterface de l’objet d’agrégation, si la IID requête n’est pas l’un des éléments natifs IIDpris en charge par l’objet CCmdTarget lui-même.

Pour utiliser la macro INTERFACE_AGGREGATE

  1. Déclarez une variable membre (un IUnknown*) qui contiendra un pointeur vers l’objet d’agrégation.

  2. Incluez une macro INTERFACE_AGGREGATE dans votre carte d’interface, qui fait référence à la variable membre par nom.

  3. À un moment donné (généralement pendant CCmdTarget::OnCreateAggregates), initialisez la variable membre sur une valeur autre que NULL.

Par exemple:

class CAggrExample : public CCmdTarget
{
public:
    CAggrExample();

protected:
    LPUNKNOWN m_lpAggrInner;
    virtual BOOL OnCreateAggregates();

    DECLARE_INTERFACE_MAP()
    // "native" interface part macros may be used here
};

CAggrExample::CAggrExample()
{
    m_lpAggrInner = NULL;
}

BOOL CAggrExample::OnCreateAggregates()
{
    // wire up aggregate with correct controlling unknown
    m_lpAggrInner = CoCreateInstance(CLSID_Example,
        GetControllingUnknown(), CLSCTX_INPROC_SERVER,
        IID_IUnknown, (LPVOID*)&m_lpAggrInner);

    if (m_lpAggrInner == NULL)
        return FALSE;
    // optionally, create other aggregate objects here
    return TRUE;
}

BEGIN_INTERFACE_MAP(CAggrExample, CCmdTarget)
    // native "INTERFACE_PART" entries go here
    INTERFACE_AGGREGATE(CAggrExample, m_lpAggrInner)
END_INTERFACE_MAP()

La variable m_lpAggrInner est initialisée dans le constructeur sur NULL. L’infrastructure ignore une variable membre NULL dans l’implémentation par défaut de QueryInterface. OnCreateAggregates est un bon endroit pour créer vos objets d’agrégation. Vous devrez l’appeler explicitement si vous créez l’objet en dehors de l’implémentation MFC de COleObjectFactory. La raison de la création d’agrégats ainsi CCmdTarget::OnCreateAggregates que de l’utilisation de ces objets devient évidente lors de la création d’objets CCmdTarget::GetControllingUnknown aggregatables est abordée.

Cette technique donnera à votre objet toutes les interfaces prises en charge par l’objet d’agrégation et ses interfaces natives. Si vous souhaitez uniquement un sous-ensemble des interfaces prises en charge par l’agrégat, vous pouvez remplacer CCmdTarget::GetInterfaceHook. Cela vous permet de très faible niveau de hookability, similaire à QueryInterface. En règle générale, vous souhaitez toutes les interfaces prises en charge par l’agrégat.

Création d’une implémentation d’objet aggregatable

Pour qu’un objet soit aggregatable, l’implémentation de AddRef, Release et QueryInterface doit déléguer à un « contrôle inconnu ». En d’autres termes, pour qu’il fait partie de l’objet, il doit déléguer AddRef, Release et QueryInterface à un autre objet, également dérivé d’IUnknown. Ce « contrôle inconnu » est fourni à l’objet lorsqu’il est créé, c’est-à-dire qu’il est fourni à l’implémentation de COleObjectFactory. L’implémentation de ce paramètre entraîne une faible surcharge et, dans certains cas, n’est pas souhaitable, de sorte que MFC le rend facultatif. Pour permettre à un objet d’être aggregatable, vous appelez CCmdTarget::EnableAggregation à partir du constructeur de l’objet.

Si l’objet utilise également des agrégats, vous devez également être sûr de passer le « contrôle inconnu » correct aux objets d’agrégation. En règle générale, ce pointeur IUnknown est passé à l’objet lors de la création de l’agrégat. Par exemple, le paramètre pUnkOuter est le « contrôle inconnu » pour les objets créés avec CoCreateInstance. Le pointeur « contrôle inconnu » correct peut être récupéré en appelant CCmdTarget::GetControllingUnknown. Toutefois, la valeur retournée par cette fonction n’est pas valide pendant le constructeur. Pour cette raison, il est suggéré de créer vos agrégats uniquement dans un remplacement de CCmdTarget::OnCreateAggregates, où la valeur de GetControllingUnknown retour est fiable, même si elle est créée à partir de l’implémentation COleObjectFactory .

Il est également important que l’objet manipule le nombre de références correct lors de l’ajout ou de la libération du nombre de références artificielles. Pour vous assurer qu’il s’agit du cas, appelez ExternalAddRef toujours et ExternalRelease au lieu de InternalRelease et InternalAddRef. Il est rare d’appeler InternalRelease ou InternalAddRef sur une classe qui prend en charge l’agrégation.

Documents de référence

L’utilisation avancée d’OLE, telle que la définition de vos propres interfaces ou la substitution de l’implémentation de l’infrastructure des interfaces OLE nécessite l’utilisation du mécanisme de mappage d’interface sous-jacent.

Cette section décrit chaque macro et les API utilisées pour implémenter ces fonctionnalités avancées.

CCmdTarget ::EnableAggregation — Description de la fonction

void EnableAggregation();

Remarques

Appelez cette fonction dans le constructeur de la classe dérivée si vous souhaitez prendre en charge l’agrégation OLE pour les objets de ce type. Cela prépare une implémentation IUnknown spéciale requise pour les objets aggregatables.

CCmdTarget ::ExternalQueryInterface — Description de la fonction

DWORD ExternalQueryInterface(
    const void FAR* lpIID,
    LPVOIDFAR* ppvObj
);

Paramètres

lpIID
Pointeur lointain vers un IID (premier argument de QueryInterface)

ppvObj
Pointeur vers un IUnknown* (deuxième argument à QueryInterface)

Remarques

Appelez cette fonction dans votre implémentation d’IUnknown pour chaque interface que votre classe implémente. Cette fonction fournit l’implémentation standard basée sur les données de QueryInterface basée sur la carte d’interface de votre objet. Il est nécessaire de convertir la valeur de retour en HRESULT. Si l’objet est agrégé, cette fonction appelle le « contrôle iUnknown » au lieu d’utiliser la carte d’interface locale.

CCmdTarget ::ExternalAddRef — Description de la fonction

DWORD ExternalAddRef();

Remarques

Appelez cette fonction dans votre implémentation d’IUnknown ::AddRef pour chaque interface que votre classe implémente. La valeur de retour est le nouveau nombre de références sur l’objet CCmdTarget. Si l’objet est agrégé, cette fonction appelle le « contrôle IUnknown » au lieu de manipuler le nombre de références locales.

CCmdTarget ::ExternalRelease — Description de la fonction

DWORD ExternalRelease();

Remarques

Appelez cette fonction dans votre implémentation d’IUnknown ::Release pour chaque interface que votre classe implémente. La valeur de retour indique le nouveau nombre de références sur l’objet. Si l’objet est agrégé, cette fonction appelle le « contrôle IUnknown » au lieu de manipuler le nombre de références locales.

DECLARE_INTERFACE_MAP — Macro Description

DECLARE_INTERFACE_MAP

Remarques

Utilisez cette macro dans n’importe quelle classe dérivée de CCmdTarget celle-ci avec une carte d’interface. Utilisé de la même façon que DECLARE_MESSAGE_MAP. Cet appel de macro doit être placé dans la définition de classe, généralement dans un en-tête (. Fichier H) . Une classe avec DECLARE_INTERFACE_MAP doit définir le mappage d’interface dans le fichier d’implémentation (. CPP) avec les macros BEGIN_INTERFACE_MAP et END_INTERFACE_MAP.

BEGIN_INTERFACE_PART et END_INTERFACE_PART — Descriptions des macros

BEGIN_INTERFACE_PART(localClass, iface);
END_INTERFACE_PART(localClass)

Paramètres

localClass
Nom de la classe qui implémente l’interface

iface
Nom de l’interface que cette classe implémente

Remarques

Pour chaque interface que votre classe implémentera, vous devez disposer d’une paire BEGIN_INTERFACE_PART et END_INTERFACE_PART. Ces macros définissent une classe locale dérivée de l’interface OLE que vous définissez ainsi qu’une variable membre incorporée de cette classe. Les membres AddRef, Release et QueryInterface sont déclarés automatiquement. Vous devez inclure les déclarations des autres fonctions membres qui font partie de l’interface en cours d’implémentation (ces déclarations sont placées entre les macros BEGIN_INTERFACE_PART et END_INTERFACE_PART).

L’argument iface est l’interface OLE que vous souhaitez implémenter, par IAdviseSinkexemple , ou IPersistStorage (ou votre propre interface personnalisée).

L’argument localClass est le nom de la classe locale qui sera définie. Un « X » est automatiquement ajouté au nom. Cette convention d’affectation de noms est utilisée pour éviter les collisions avec les classes globales du même nom. En outre, le nom du membre incorporé, identique au nom localClass , sauf qu’il est précédé de « m_x ».

Par exemple:

BEGIN_INTERFACE_PART(MyAdviseSink, IAdviseSink)
    STDMETHOD_(void, OnDataChange)(LPFORMATETC, LPSTGMEDIUM);
    STDMETHOD_(void, OnViewChange)(DWORD, LONG);
    STDMETHOD_(void, OnRename)(LPMONIKER);
    STDMETHOD_(void, OnSave)();
    STDMETHOD_(void, OnClose)();
END_INTERFACE_PART(MyAdviseSink)

définirait une classe locale appelée XMyAdviseSink dérivée d’IAdviseSink et un membre de la classe dans laquelle elle est déclarée appelée m_xMyAdviseSink.Remarque :

Remarque

Les lignes commençant STDMETHOD_ par sont essentiellement copiées à partir d’OLE2. H et modifié légèrement. Copie de celles-ci à partir d’OLE2. H peut réduire les erreurs difficiles à résoudre.

BEGIN_INTERFACE_MAP et END_INTERFACE_MAP — Descriptions des macros

BEGIN_INTERFACE_MAP(theClass, baseClass)
END_INTERFACE_MAP

Paramètres

theClass
Classe dans laquelle le mappage d’interface doit être défini

baseClass
Classe à partir de laquelle la classe dérive.

Remarques

Les macros BEGIN_INTERFACE_MAP et END_INTERFACE_MAP sont utilisées dans le fichier d’implémentation pour définir réellement le mappage d’interface. Pour chaque interface implémentée, il existe un ou plusieurs appels de macros INTERFACE_PART. Pour chaque agrégat que la classe utilise, il existe un appel de macro INTERFACE_AGGREGATE.

INTERFACE_PART — Macro Description

INTERFACE_PART(theClass, iid, localClass)

Paramètres

theClass
Nom de la classe qui contient le mappage d’interface.

iid
Qui IID doit être mappé à la classe incorporée.

localClass
Nom de la classe locale (moins le « X »).

Remarques

Cette macro est utilisée entre la macro BEGIN_INTERFACE_MAP et la macro END_INTERFACE_MAP pour chaque interface prise en charge par votre objet. Il vous permet de mapper un IID à un membre de la classe indiquée par la classe et localClass. Le « m_x » est ajouté automatiquement à la classe locale . Notez que plusieurs IID peuvent être associés à un seul membre. Cela est très utile lorsque vous implémentez uniquement une interface « la plus dérivée » et souhaitez également fournir toutes les interfaces intermédiaires. Voici un bon exemple de l’interface IOleInPlaceFrameWindow . Sa hiérarchie ressemble à ceci :

IUnknown
    IOleWindow
        IOleUIWindow
            IOleInPlaceFrameWindow

Si un objet implémente IOleInPlaceFrameWindow, un client peut QueryInterface sur l’une de ces interfaces : IOleUIWindow, IOleWindowou IUnknown, en plus de l’interface IOleInPlaceFrameWindow « la plus dérivée » (celle que vous implémentez réellement). Pour gérer cela, vous pouvez utiliser plusieurs macros INTERFACE_PART pour mapper chaque interface de base à l’interface IOleInPlaceFrameWindow :

dans le fichier de définition de classe :

BEGIN_INTERFACE_PART(CMyFrameWindow, IOleInPlaceFrameWindow)

dans le fichier d’implémentation de classe :

BEGIN_INTERFACE_MAP(CMyWnd, CFrameWnd)
    INTERFACE_PART(CMyWnd, IID_IOleWindow, MyFrameWindow)
    INTERFACE_PART(CMyWnd, IID_IOleUIWindow, MyFrameWindow)
    INTERFACE_PART(CMyWnd, IID_IOleInPlaceFrameWindow, MyFrameWindow)
END_INTERFACE_MAP

Le framework s’occupe d’IUnknown, car il est toujours nécessaire.

INTERFACE_PART — Macro Description

INTERFACE_AGGREGATE(theClass, theAggr)

Paramètres

theClass
Nom de la classe qui contient le mappage d’interface,

theAggr
Nom de la variable membre à agréger.

Remarques

Cette macro est utilisée pour indiquer au framework que la classe utilise un objet d’agrégation. Il doit apparaître entre les macros BEGIN_INTERFACE_PART et END_INTERFACE_PART. Un objet d’agrégation est un objet distinct, dérivé d’IUnknown. En utilisant un agrégat et la macro INTERFACE_AGGREGATE, vous pouvez faire en sorte que toutes les interfaces prises en charge par l’agrégat apparaissent directement prises en charge par l’objet. L’argumentAggr est simplement le nom d’une variable membre de votre classe qui est dérivée d’IUnknown (directement ou indirectement). Toutes les macros INTERFACE_AGGREGATE doivent suivre les macros INTERFACE_PART lorsqu’elles sont placées dans une carte d’interface.

Voir aussi

Notes techniques par numéro
Notes techniques par catégorie