TN038 : Implémentation MFC/OLE IUnknown

[!REMARQUE]

La note technique suivante n'a pas été modifiée depuis si c'était première inclus dans la documentation en ligne.Par conséquent, certaines procédures et rubriques peuvent être obsolètes ou incorrects.Pour obtenir les informations les plus récentes, il est recommandé que vous trouviez la rubrique d'intérêt dans l'index de la documentation en ligne.

Au cœur OLE 2 est « OLE modèle objet composant », ou COM.COM définit une norme sur la façon dont les objets coopératifs communiquent les uns aux autres.Cela inclut les détails concernant ce qu'un « objet » ressemble à, y compris comment les méthodes sont distribuées sur un objet.COM définit également une classe de base, de laquelle toutes les classes compatibles COM sont dérivées.Cette classe de base est IUnknown.Bien que l'interface d' IUnknown soit indiquée comme une classe C++, COM n'est pas spécifique à un langage — elle peut être implémentée 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 en tant que « interfaces. » Cette distinction est importante, car une « interface » telles que IUnknown ne distribue avec elle pas d'implémentation.Elle définit simplement le protocole par lequel les objets communiquent, pas les caractéristiques de ce que ces implémentations font.Cela est utile pour un système qui permet la flexibilité maximale.Il vaut travail MFC d'implémenter un comportement par défaut pour les programmes de MFC/C++.

Pour comprendre l'implémentation MFC d' IUnknown vous devez d'abord comprendre ce que sont 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 nécessaires de la convention d'appel, tels qu' __stdcall sont exclus pour cette illustration.

Les fonctions membres d' AddRef et de Release contrôlent la gestion de la mémoire de l'objet.COM utilise un modèle de décompte de références de conserver des objets.Un objet n'est jamais directement référencé comme vous en C++.À la place, les objets COM sont toujours référencés par l'intermédiaire d'un pointeur.Pour libérer l'objet lorsque le propriétaire est fait de les utiliser celle-ci, le membre de Release de l'objet est appelé (par opposition à utiliser la suppression d'opérateur, comme est fait pour un objet traditionnel C++).Le mécanisme de décompte de références propose de nombreuses références à un objet unique à gérer.Une implémentation d' AddRef et de Release gère un décompte de références sur l'objet — l'objet n'est pas supprimé jusqu'à ce que ses atteint zéro de décompte de références.

AddRef et Release sont assez simples d'un point d'implémentation.Voici une implémentation simple :

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

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

La fonction membre d' QueryInterface est un peu plus intéressante.Il n'est pas très utile d'avoir un objet dont les seules fonctions membres sont AddRef et Release — il ferait beau de déterminer l'objet d'effectuer des opérations qu' IUnknown le fournit.C'est QueryInterface est utile.Il vous permet d'obtenir une « interface » différente 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 comme pur-virtuelles.Par exemple :

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

Pour obtenir IPrintInterface si vous avez uniquement IUnknown, appelez IUnknown::QueryInterface à l'aide de IID d' IPrintInterface.IID est un nombre de bits qui identifie l'interface.Il existe IID pour chaque interface que vous ou OLE définit.Si pUnk est un pointeur vers un objet d' IUnknown , le code pour récupérer IPrintInterface de celle-ci peut être :

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

Cela semble-t-vous relativement simple, mais comment implémenteriez-vous un objet qui prend en charge l'interface d'IPrintInterface et d' IUnknown ?Dans ce cas il est simple étant donné que l'IPrintInterface est directement dérivé d' 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 de Release sont exactement les mêmes que celles implémentées ci-dessus.CPrintObj::QueryInterface ressemble à 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 identificateur d'interface (IID) est identifié, un pointeur est retourné à votre objet ; sinon une erreur se produit.Notez également qu' QueryInterface réussie se traduit par AddRefimplicite.Naturellement, vous devez également implémenter CEditObj::Print.C'est simple car IPrintInterface était directement dérivé de l'interface d' IUnknown .Toutefois si vous souhaitez prendre en charge deux interfaces différentes, les deux dérivé d' IUnknown, considérez les éléments suivants :

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

Bien qu'il y a différentes façons d'implémenter une classe prenant en charge IEditInterface et IPrintInterface, y compris à l'héritage multiple C++, cette remarque se concentre sur l'utilisation des classes imbriquées pour implémenter ces fonctionnalités.

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'implémentation entière 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 majeure partie de l'implémentation d' IUnknown se place dans la classe de CEditPrintObj plutôt que dupliquant code dans CEditPrintObj::CEditObj et CEditPrintObj::CPrintObj.Cela réduit la quantité de code et évite des bogues.Le point clé ici est celle de l'interface d'IUnknown qu'il est possible d'appeler QueryInterface pour extraire une interface 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 d' QueryInterface disponibles dans chaque interface doivent se comporter exactement de la même façon.Pour que ces objets incorporés appeler l'implémentation dans « l'objet externe », un dos-pointeur est utilisé (m_pParent).Le pointeur m_pParent est initialisé pendant le constructeur de CEditPrintObj.Vous montrer CEditPrintObj::CPrintObj::PrintObject et CEditPrintObj::CEditObj::EditObject également.Un bit de code a été ajouté assez pour ajouter une fonctionnalité — la possibilité de modifier l'objet.Heureusement, il est assez rare que les interfaces ont une seule fonction membre unique (bien qu'elle se produise) et dans ce cas, EditObject et PrintObject sont généralement regroupés dans une interface unique.

Il est beaucoup d'explications 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 aux fenêtres de façon dont les messages sont encapsulés avec les tables des messages.Cette fonctionnalité est appelée les mappages d'interface et est expliquée dans la section suivante.

Mappages d'interface MFC

MFC/OLE inclut une implémentation de « mappages d'interface » semblables aux « tables des messages » MFC et aux « tables de dispatch » dans le concept et l'exécution.Les principales fonctionnalités des mappages d'interface MFC sont les suivantes :

  • Une implémentation standard d' IUnknown, intégrée à CCmdTarget la classe.

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

  • Implémentation pilotée par les données d' QueryInterface

En outre, les mappages d'interface prennent en charge les fonctionnalités avancées suivante :

  • Prise en charge de créer des objets COM pouvant être regroupé en agrégats

  • Prise en charge de l'utilisation des objets globaux dans l'implémentation d'un objet COM

  • l'implémentation est hookable et extensible

Pour plus d'informations sur le regroupement, consultez la notion de référence du programmeur.

La prise en charge du mappage d'interfaces MFC est associée à une racine dans la classe d' CCmdTarget .CCmdTarget « a-un » le décompte de références ainsi que toutes les fonctions membres associées à l'implémentation d' IUnknown (le décompte de références par exemple est dans CCmdTarget).Pour créer une classe qui prend en charge OLE COM, dérivez une classe d' CCmdTarget et utilisez différentes macros ainsi que les fonctions membres d' CCmdTarget pour implémenter des interfaces souhaitées.L'implémentation MFC utilise les classes imbriquées pour définir chaque implémentation d'interface de façon très similaire à l'exemple ci-dessus.Cela est facilité avec une implémentation standard d'IUnknown ainsi qu'un certain nombre de macros qui élimine une partie du code répétitif.

Pour implémenter une classe à l'aide de les mappages d'interface MFC

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

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

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

  4. Dans le fichier d'implémentation, utilisez les macros d' BEGIN_INTERFACE_MAP et d' END_INTERFACE_MAP pour définir le mappage d'interfaces de la classe.

  5. Pour chaque IID pris en charge, utilisez la macro d' INTERFACE_PART entre BEGIN_INTERFACE_MAP et les macros d' END_INTERFACE_MAP pour mapper qu'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 d' METHOD_PROLOGUE pour accéder au parent, CCmdTargetobjet dérivé.

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

L'exemple de 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 d' CCmdTarget.La macro de DECLARE_INTERFACE_MAP indique à l'infrastructure que cette classe aura un mappage d'interfaces personnalisé.En outre, les macros de BEGIN_INTERFACE_PART et d' END_INTERFACE_PART définissent des classes imbriquées, dans ce cas avec les noms CEditObj et CPrintObj (X est utilisé pour différencier uniquement des classes imbriquées de classes globales qui commencent par « C » et des 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 AddRef, Release, et les fonctions d' QueryInterface ; par conséquent vous déclarez seules les fonctions spécifiques à cette interface : EditObject et PrintObject (OLE macro STDMETHOD est utilisé ces afin que _stdcall les mots clés et virtuels ne disposent de façon appropriée pour la plateforme cible).

Pour implémenter le mappage d'interfaces de cette classe :

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

Cela connecte l'IID_IPrintInterface IID avec le m_CPrintObj et IID_IEditInterface avec le m_CEditObj respectivement.L'implémentation d' CCmdTarget d' QueryInterface (CCmdTarget::ExternalQueryInterface) utilise ce mappage pour retourner des pointeurs vers le m_CPrintObj et au m_CEditObj une fois demandée.Il n'est pas nécessaire de comprendre une entrée pour IID_IUnknown; l'infrastructure utilise la première interface dans le mappage (dans ce cas, m_CPrintObj) lorsque IID_IUnknown est demandé.

Bien que la macro de BEGIN_INTERFACE_PART a automatiquement déclaré AddRef, Release et des fonctions d' 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 pour CEditPrintObj::CPrintObj, est semblable aux définitions ci-dessus pour CEditPrintObj::CEditObj.Bien qu'il soit possible de créer une macro qui peut être utilisée pour générer automatiquement ces fonctions (mais précédemment dans le développement de MFC/OLE cela a été la casse), il est 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 d'infrastructure des tables des messages, il existe plusieurs événements qui n'étaient pas nécessaires pour que :

  • Implémentez QueryInterface

  • Implémentez AddRef et le diffuser

  • Déclarez l'un ou l'autre de ces méthodes intégrées à la fois les interfaces

De plus, l'infrastructure utilise des tables des messages en interne.Cela vous permet de dériver d'une classe d'infrastructure, de déterminer COleServerDoc, qui prend déjà en charge certaines interfaces et fournit des remplacements ou les ajouts aux interfaces fournies par l'infrastructure.Cela est vérifié par le fait que le prend en charge d'infrastructure entièrement permettant à un mappage d'interfaces d'une classe de base qui désigne la raison pour laquelle BEGIN_INTERFACE_MAP prend comme son 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 MFC des interfaces OLE en héritant la spécialisation incorporée de cette interface de la version MFC.Ce n'est pas possible parce que l'utilisation de la macro de METHOD_PROLOGUE d'obtenir l'accès à CCmdTargetcontenant l'objet dérivé implique fixed offset de l'objet incorporé d' CCmdTargetobjet dérivé.Ce biais, par exemple, vous ne pouvez pas dériver de XMyAdviseSink incorporé de l'implémentation MFC dans COleClientItem::XAdviseSink, car XAdviseSink repose sur être à un offset spécifique du haut de l'objet d' COleClientItem .

[!REMARQUE]

Vous pouvez, toutefois, la délégation à l'implémentation MFC pour toutes les fonctions que vous souhaitez rétablir le comportement par défaut MFC.Cela s'effectue dans l'implémentation MFC d' IOleInPlaceFrame (XOleInPlaceFrame) dans la classe de COleFrameHook (il délègue au m_xOleInPlaceUIWindow pour de nombreuses fonctions).Cette conception a été sélectionné pour réduire la taille d'exécution des objets qui implémentent de nombreuses interfaces ; il élimine le besoin de dos-pointeur (telle que la façon m_pParent a été utilisé dans la section précédente).

5hhehwba.collapse_all(fr-fr,VS.110).gifRegroupement et mappages d'interface

En plus de prendre en charge les objets COM autonomes, MFC prend également en charge le regroupement.Le regroupement lui-même est un sujet trop complexe à discuter ici ; reportez -vous à la notion de référence du programmeur pour plus d'informations sur le regroupement.Cette remarque décrira simplement en charge le regroupement intégré dans l'infrastructure et les mappages d'interface.

Il existe deux façons d'utiliser le regroupement : (1) à l'aide d'un objet COM qui prend en charge le regroupement, et (2) en implémentant un objet qui peut être regroupé par un autre.Ces fonctions peuvent être référencées comme « à l'aide d'un objet global » et « qu'un objet aggregatable ».MFC prend en charge les deux.

5hhehwba.collapse_all(fr-fr,VS.110).gifÀ l'aide d'un objet global

Pour utiliser un objet global, il faut un moyen d'attacher l'agrégat dans le mécanisme de QueryInterface.En d'autres termes, l'objet global doit se comporter comme s'il s'agit d'une partie native de votre objet.Donc comment clôturées ce lien dans le mécanisme de mappage d'interfaces MFC ?En plus de la macro de INTERFACE_PART , où un objet imbriqué est mappé à un IID, vous pouvez également déclarer un objet global dans le cadre de votre classe dérivée d' CCmdTarget .Pour cela, la macro d' INTERFACE_AGGREGATE est utilisée.Cela vous permet de spécifier une variable membre (qui doit être un pointeur vers IUnknown ou classe dérivée), qui doit être intégré au mécanisme de mappage d'interfaces.Si le pointeur n'est pas NULL lorsque CCmdTarget::ExternalQueryInterface est appelé, l'infrastructure appelle automatiquement la fonction membre d' QueryInterface de l'objet global, si IID demandé ne s'agit pas d' IIDnatif s pris en charge par l'objet d' CCmdTarget lui-même.

Pour utiliser la macro d'INTERFACE_AGGREGATE

  1. Déclarez une variable membre ( IUnknown*) qui contiendra un pointeur vers l'objet global.

  2. Incluez une macro d' INTERFACE_AGGREGATE dans le mappage d'interface, qui fait référence à la variable de membre nom.

  3. À un moment donné (en général lors de le CCmdTarget::OnCreateAggregates), initialisez la variable membre à un élément 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()

m_lpAggrInner est initialisé dans le constructeur une valeur NULL.L'infrastructure ignore l'une variable membre NULL dans l'implémentation par défaut d' QueryInterface.OnCreateAggregates est un emplacement idéal pour créer réellement vos objets globaux.Vous devez l'appeler explicitement si vous créez l'objet en dehors de l'implémentation MFC d' COleObjectFactory.La raison de créer des agrégats dans CCmdTarget::OnCreateAggregates ainsi que l'utilisation de CCmdTarget::GetControllingUnknown devient visible lorsqu'il crée des objets pouvant être regroupé en agrégats est abordée.

Cette technique fournira à votre objet toutes les interfaces que l'objet global prend en charge ses interfaces natives.Si vous souhaitez uniquement un sous-ensemble des interfaces que l'agrégat prend en charge, vous pouvez substituer CCmdTarget::GetInterfaceHook.Cela vous permet le hookability de très bas niveau, semblable à QueryInterface.En général, vous souhaitez toutes les interfaces que l'agrégat prend en charge.

5hhehwba.collapse_all(fr-fr,VS.110).gifFaisant une implémentation d'objet aggregatable

Pour qu'un objet soit aggregatable, l'implémentation de AddRef, Release, et QueryInterface doivent les déléguer à « contrôler l'inconnu ». En d'autres termes, pour qu'il fasse partie de l'objet, elle doit déléguer AddRef, Release, et QueryInterface à un objet différent, également dérivé d' IUnknown.This « qui contrôle l'inconnu » est fourni à l'objet lorsqu'il est créé, il est fourni c. autrement dit., à l'implémentation d' COleObjectFactory.Implémentant il comporte une petite surcharge, et non dans certains cas souhaitable, donc MFC facilite cette facultatif.Pour permettre à un objet pour être aggregatable, vous appelez CCmdTarget::EnableAggregation du constructeur de l'objet.

Si l'objet utilise également des agrégats, vous devez également vous assurer de passer le droite « contrôlant l'inconnu » aux objets globaux.Généralement ce pointeur d' IUnknown est passé à l'objet lorsque l'agrégat est créé.Par exemple, le paramètre de pUnkOuter est « contrôler l'inconnu » pour les objets créés avec CoCreateInstance.Le approprié « contrôle » le pointeur inconnu peut être récupéré en appelant CCmdTarget::GetControllingUnknown.La valeur retournée par cette fonction, toutefois, n'est pas valide pendant le constructeur.Pour cette raison, on suggère que vous avez créé vos agrégats uniquement dans une substitution de CCmdTarget::OnCreateAggregates, où la valeur de retour d' GetControllingUnknown est fiable, même si créé à partir de l'implémentation d' COleObjectFactory .

Il est également important que l'objet manipulent le décompte de références est correct en ajoutant ou en libérant des numéros de référence artificiels.Pour garantir cela est le cas, toujours appel ExternalAddRef et ExternalRelease au lieu d' InternalRelease et d' InternalAddRef.Il est rare d'appeler InternalRelease ou InternalAddRef dans une classe qui prend en charge le regroupement.

5hhehwba.collapse_all(fr-fr,VS.110).gifDocuments de référence

L'utilisation avancée OLE, tel que définir vos propres interfaces ou substituer l'implémentation de l'infrastructure des interfaces OLE nécessite l'utilisation du mécanisme sous-jacent de mappage d'interfaces.

Cette section traite chaque macro et les API utilisée pour implémenter cette fonctionnalité avancée.

5hhehwba.collapse_all(fr-fr,VS.110).gifCCmdTarget::EnableAggregation — description de fonction

void EnableAggregation();

Remarques

Appelez cette fonction dans le constructeur de la classe dérivée si vous souhaitez prendre en charge le regroupement OLE pour les objets de ce type.Cela prépare une implémentation particulière d'IUnknown requise pour les objets pouvant être regroupé en agrégats.

5hhehwba.collapse_all(fr-fr,VS.110).gifCCmdTarget::ExternalQueryInterface — description de fonction

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

Remarques

5hhehwba.collapse_all(fr-fr,VS.110).gifParamètres

  • lpIID
    Un pointeur lointain à un IID (le premier argument d'appeler QueryInterface)

  • ppvObj
    Un pointeur vers un IUnknown* (deuxième argument d'appeler QueryInterface)

Remarques

Appelez cette fonction dans votre implémentation d'IUnknown pour chaque interface que la classe implémente.Cette fonction fournit l'implémentation pilotée par des données standard pour appeler QueryInterface selon le mappage d'interfaces de votre objet.Il est nécessaire de caster la valeur de retour à un HRESULT.Si l'objet est regroupé, cette fonction appellera « contrôle » IUnknown au lieu d'utiliser le mappage d'interfaces local.

5hhehwba.collapse_all(fr-fr,VS.110).gifCCmdTarget::ExternalAddRef — description de fonction

DWORD ExternalAddRef();

Remarques

Appelez cette fonction dans votre implémentation d'IUnknown::AddRef pour chaque interface que la classe implémente.La valeur de retour est le nouveau nombre de références sur l'objet de CCmdTarget.Si l'objet est regroupé, cette fonction appellera « contrôle » IUnknown au lieu de manipuler le décompte de références local.

5hhehwba.collapse_all(fr-fr,VS.110).gifCCmdTarget::ExternalRelease — description de fonction

DWORD ExternalRelease();

Remarques

Appelez cette fonction dans votre implémentation d'IUnknown::Release pour chaque interface que la classe implémente.La valeur de retour indique le nouveau nombre de références de l'objet.Si l'objet est regroupé, cette fonction appellera « contrôle » IUnknown au lieu de manipuler le décompte de références local.

5hhehwba.collapse_all(fr-fr,VS.110).gifDECLARE INTERFACE MAP - macro description

DECLARE_INTERFACE_MAP

Remarques

Utilisez cette macro dans toute classe dérivée d' CCmdTarget qui aura un mappage d'interfaces.Utilisé à peu près de la même façon que DECLARE_MESSAGE_MAP.Cette macro appel doit être placé dans la définition de classe, habituellement dans un en-tête (. h) fichier.Une classe avec DECLARE_INTERFACE_MAP doit définir la table d'interface dans le fichier d'implémentation (.CPP) avec les macros d' BEGIN_INTERFACE_MAP et d' END_INTERFACE_MAP .

5hhehwba.collapse_all(fr-fr,VS.110).gifBEGIN_INTERFACE_PART et END_INTERFACE_PART — macro descriptions

BEGIN_INTERFACE_PART( 
   localClass,
   iface 
);
END_INTERFACE_PART( 
   localClass 
)

Remarques

5hhehwba.collapse_all(fr-fr,VS.110).gifParamètres

  • localClass
    Le nom de la classe qui implémente l'interface

  • iface
    Le nom de l'interface que cette classe implémente

Remarques

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

L'argument d'iface est OLE interface que vous souhaitez implémenter, notamment IAdviseSink, ou IPersistStorage (ou votre propre interface personnalisée).

L'argument de localClass est le nom de la classe locale qui doit être définie.Un « x » sera ajouté automatiquement au nom.Cette convention d'affectation de noms est utilisée pour éviter des collisions avec les classes globales du même nom.En outre, le nom du membre incorporé, même que les localClass nom sauf s'il est préfixé par le « 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é d'IAdviseSink, et un membre de la classe dans laquelle il est déclaré m_xMyAdviseSink.Note appelé :

[!REMARQUE]

Les lignes commençant par STDMETHOD_ sont essentiellement copiées d'OLE2.H et légèrement modifiées.Les copier d'OLE2.H peuvent réduire les erreurs il est difficile de la résoudre que.

5hhehwba.collapse_all(fr-fr,VS.110).gifBEGIN_INTERFACE_MAP et END_INTERFACE_MAP — macro descriptions

BEGIN_INTERFACE_MAP( 
   theClass,
   baseClass 
)
END_INTERFACE_MAP

Remarques

5hhehwba.collapse_all(fr-fr,VS.110).gifParamètres

  • theClass
    La classe dans laquelle le mappage d'interface doit être défini

  • baseClass
    La classe dont theClass dérive de.

Remarques

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

5hhehwba.collapse_all(fr-fr,VS.110).gifINTERFACE PART - macro description

INTERFACE_PART( 
   theClass,
   iid, 
   localClass 
)

Remarques

5hhehwba.collapse_all(fr-fr,VS.110).gifParamètres

  • theClass
    Le nom de la classe qui contient la table d'interface.

  • iid
    IID qui doit pour être mappé à la classe incorporée.

  • localClass
    Le nom de la classe locale (moins « X ").

Remarques

Cette macro est utilisée entre le d' BEGIN_INTERFACE_MAP et le d' END_INTERFACE_MAP pour chaque interface que votre objet le prend en charge.Il vous permet de mapper un IID à un membre de la classe indiquée par theClass et des localClass.« M_x » sera ajouté aux localClass automatiquement.Notez que plusieurs IID peut être associé à un seul membre.Cela s'avère particulièrement utile lorsque vous implémentez une seule interface « la plus dérivée » et le souhaitez pour fournir toute l'intermédiaire interfaces également.En est un bon exemple l'interface d' IOleInPlaceFrameWindow .Sa hiérarchie se présente comme suit :

IUnknown
    IOleWindow
        IOleUIWindow
            IOleInPlaceFrameWindow

Si un objet implémente IOleInPlaceFrameWindow, un client peut QueryInterface sur l'un de ces interfaces : IOleUIWindow, IOleWindow, ou IUnknown, outre l'interface « la plus dérivée » IOleInPlaceFrameWindow (celui que vous implémentez réellement).Pour gérer cela vous pouvez utiliser plusieurs macro d' INTERFACE_PART pour mapper chaque interface de base à l'interface d' 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

L'infrastructure se charge d'IUnknown car elle est toujours requise.

5hhehwba.collapse_all(fr-fr,VS.110).gifINTERFACE PART - macro description

INTERFACE_AGGREGATE( 
   theClass,
   theAggr 
)

Remarques

5hhehwba.collapse_all(fr-fr,VS.110).gifParamètres

  • theClass
    Le nom de la classe qui contient la table d'interface,

  • theAggr
    Le nom de la variable membre qui doit être regroupées.

Remarques

Cette macro est utilisée pour indiquer à l'infrastructure que la classe utilise un objet global.Il doit figurer entre macros de BEGIN_INTERFACE_PART et d' END_INTERFACE_PART .Un objet global est un objet distinct, dérivé d' IUnknown.À l'aide d'un agrégat et de la macro de INTERFACE_AGGREGATE , vous pouvez effectuer toutes les interfaces qui prend en charge globales semblent être pris en charge directement par l'objet.L'argument de theAggr est simplement le nom d'une variable membre de votre classe dérivée d' IUnknown (directement ou indirectement).Toutes les macros d' INTERFACE_AGGREGATE doivent suivre les macros d' INTERFACE_PART une fois placées dans un mappage d'interfaces.

Voir aussi

Autres ressources

Notes techniques de nombres

Notes techniques de catégorie