TN038 : MFC/OLE IUnknown mise en œuvre

Au cœur de OLE 2 est le « OLE Component Object Model », ou COM. COM définit une norme pour les objets coopérants comment communiquer à l'autre. Cela comprend les détails de ce qu'un « objet » ressemble, y compris comment les méthodes sont envoyés sur un objet. COM définit également une classe de base, qui sont dérivées toutes les classes de COM compatibles. Cette classe de base est de IUnknown. Bien que l'interface IUnknown est appelée une classe C++, COM n'est pas spécifique à une un langue — il peut être implémenté en C, PASCAL ou toute autre langue qui peut prendre en charge la disposition binaire d'un objet COM.

OLE se réfère à toutes les classes dérivées de IUnknown comme « interfaces ». C'est une distinction importante, depuis une « interface », telles que IUnknown est assortie d'aucune implémentation. Il définit simplement le protocole par lequel les objets communiquent, pas les détails de ce que font ces implémentations. C'est raisonnable pour un système qui permet une flexibilité maximale. C'est le travail de MFC pour mettre en oeuvre un comportement par défaut pour les programmes C++/MFC.

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

 classe IUnknown
{
public :
 nbsp ;  Virtual HRESULT QueryInterface(REFIID iid, void** ppvObj) = 0 ;
    Virtual ULO&NG AddRef() = 0 ;
    Virtual ULONG Release() = 0 ;
}

&Notenbsp ;  Certains détails convention appelant nécessaires, tels que __stdcall sont laissés pour cette illustration.

Les fonctions membres AddRef et Release contrôlent de gestion de la mémoire de l'objet. COM utilise un système de comptage de référence pour suivre les 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 est effectué à l'aide, membre de libération de l'objet est appelé (par opposition à l'aide de la suppression de l'opérateur, comme le ferait pour un objet C++ traditionnel). Le mécanisme de décompte des références permettant de multiples références à un objet unique qui sera géré. Une implémentation de AddRef et Release conserve un décompte de références sur l'objet, l'objet n'est pas supprimé avant son décompte atteint zéro.

AddRef et Release sont assez simples du point de vue de la mise en œuvre. Voici une implémentation triviale:

ULO&NG CMyObj::AddRef() {nbsp ;  retour ++ m_dwRef ; 
}

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

La fonction de membre de QueryInterface est un peu plus intéressante. Comme vous pouvez l'imaginer, il n'est pas très intéressant d'avoir un objet dont les fonctions membres seulement sont AddRef et Release — il serait bon d'indiquer l'objet de faire plus de choses que IUnknown fournit. C'est où 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 de IUnknown et ajoutent des fonctionnalités supplémentaires en ajoutant de nouvelles fonctions membres. Les interfaces COM jamais déclarées dans l'interface des variables de membre, et toutes les fonctions de membre sont déclarées comme pure virtual. Par exemple,

class IPri&ntInterface : public IUnknown
{
public :
 nbsp ;  Virtual PrintObject() nulle = 0 ;
}

Pour obtenir un IPrintInterface si vous n'avez qu'un IUnknown, appeler IUnknown::QueryInterface utilisant l' IID de l'IPrintInterface. Un IID est un nombre de 128 bits qui identifie de manière unique l'interface. Il y a un IID pour chaque interface que vous ou OLE définir. Si le pUnk est un pointeur vers un objet IUnknown , le code pour extraire un IPrintInterface pourrait être

IPrintInterface * pPrint = NULL ;
Si (pUnk-gt ;QueryInterface(IID_IPrintInterface, (void**) & pPrint) == NOERROR)
{
    pPrint - > PrintObject() ;
    pPrint - > Release() ;   
        / / pointeur de sortie obtenu via QueryInterface
}

Cela semble assez facile, mais comment est-ce que vous implémenteriez un objet prenant en charge une interface IUnknown et de la IPrintInterface ? Dans ce cas, c'est simple puisque le IPrintInterface est directement dérivé de IUnknown — en mettant en œuvre IPrintInterface, IUnknown bénéficie automatiquement. Par exemple:

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

Les implémentations de AddRef et Release serait exactement les mêmes que ceux mis en œuvre plus haut. CPrintObj::QueryInterface serait quelque chose comme ça

HRESULT CPrintObj::QueryInterface(REFIID iid, void FAR* FAR* ppvObj)
{
 nbsp ;  Si (iid == IID_IUnknown || iid == IID_IPrintInterface)
    {
        * ppvObj = this ;
        AddRef() ;
        retourne &NOERROR ;
    }
    Return ResultFromScode(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'un succès QueryInterface entraîne un implicite AddRef. Bien sûr, vous devrez également mettre en œuvre des CEditObj::Print. C'est simple, parce que le IPrintInterface était directement dérivée de l'interface IUnknown . Cependant si vous souhaitez soutenir deux interfaces différentes, toutes deux dérivées IUnknown, considérer les éléments suivants

class IEditI&nterface : public IUnkown
{
public :
 nbsp ;  Virtual EditObject() nulle = 0 ;
}

Bien qu'il existe un certain nombre de différentes façons d'implémenter une classe IEditInterface et IPrintInterface, y compris à l'aide de C++ héritage multiple, cette note se concentrera sur l'utilisation des classes imbriquées pour implémenter cette fonctionnalité.

classe CEditPrintObj
{
public :
 nbsp ;  CEditPrintObj() ;

HRESULT QueryInterface(REFIID iid, void**) ;
    ULO&NG 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 ;
}

La mise en œuvre entière est inclus ci-dessous:

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

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

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

HRESULT CEditPrintObj::QueryInterface(REFIID iid, void** ppvObj)
{
    Si (iid == IID_IUnknown || iid == IID_IPrintInterface)
    {
        * ppvObj = & m_printObj ;
        AddRef() ;
        retourne NOERROR ;
    }
    else if (iid == IID_IEditInterface)
    {
        * ppvObj = & m_editObj ;
        AddRef() ;
        retourne NOERROR ;
    }
    Return ResultFromScode(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 la mise en œuvre de IUnknown est placé dans la classe de CEditPrintObj plutôt que de dupliquer le code dans CEditPrintObj::CEditObj et CEditPrintObj::CPrintObj. Cela réduit la quantité de code et évite les bugs. Le point clé ici est qu'il est possible d'appeler QueryInterface pour récupérer tout l'objet peut prendre en charge l'interface de l'interface IUnknown et de chacune de ces interfaces, il est possible de faire de même. Cela signifie que toutes les fonctions de QueryInterface disponibles de chaque interface doivent se comportent exactement de la même façon. Afin que ces objets incorporés appeler la mise en œuvre de le « objet externe », un retour de pointeur est utilisé (m_pParent). Le pointeur m_pParent est initialisé dans le constructeur CEditPrintObj. Alors que vous mettriez en CEditPrintObj::CPrintObj::PrintObject et CEditPrintObj::CEditObj::EditObject ainsi. Un peu de code a été ajouté pour ajouter une fonctionnalité — la capacité de modifier l'objet. Heureusement, il est assez rare pour les interfaces d'avoir seulement une fonction de membre unique (bien qu'il arrive), et dans ce cas, EditObject et PrintObject devrait généralement être combinés dans une seule interface.

C'est beaucoup d'explications et beaucoup de code pour un tel scénario simple. Les classes MFC/OLE fournissent une alternative plus simple. L'application MFC utilise une technique similaire à la façon dont les messages Windows sont emballés avec des cartes de Message. Cette installation est appelée Interface cartes et est discutée dans la section suivante.

Cartes d'Interface MFC

MFC/OLE inclut une implémentation de « Interface cartes » similaire à MFC « Cartes Message » et « Envoi cartes » dans le concept et l'exécution. Voici les caractéristiques de base des cartes d'Interface MFC:

En outre, les cartes d'interface appuient suivants les fonctions avancées:

Pour plus d'informations sur l'agrégation, voir la référence du programmeur OLE du.

Interface carte prise en charge de MFC est enracinée dans la classe de CCmdTarget . CCmdTarget «a-a» référence comte ainsi que toutes les fonctions de membre associées à la mise en œuvre de IUnknown (le décompte de références est par exemple CCmdTarget). Pour créer une classe qui prend en charge OLE COM, vous dérivez une classe de CCmdTarget et utiliser des macros diverses ainsi que les fonctions membres de CCmdTarget implémenter les interfaces désirés. Application MFC utilise les classes imbriquées pour définir chaque implémentation de l'interface comme l'exemple ci-dessus. Ceci est rendu plus facile avec une implémentation standard de IUnknown, mais aussi un certain nombre de macros d'éliminer certains du code répétitif.

Notions de base de la carte d'interface

Procédez pour implémenter une classe qui utilise l'interface MFC cartes:

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

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

  3. Pour chaque interface que vous souhaitez soutenir, utiliser les macros BEGIN_INTERFACE_PART et END_INTERFACE_PART dans la définition de classe.

  4. Dans le fichier de mise en œuvre, utiliser les macros BEGIN_INTERFACE_MAP et END_INTERFACE_MAP pour définir le mappage 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 l'IID spécifique « partie » de votre classe.

  6. Mise en œuvre de chacune des classes imbriquées qui représentent les interfaces que vous soutenez.

  7. Utilisez la macro METHOD_PROLOGUE pour accéder à la société mère, CCmdTarget-objet dérivé.

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

L'exemple de CPrintEditObj ci-dessus pourrait être implémentée comme suit:

class CPrintEditObj : public CCmdTarget
{
public :
 nbsp ;  / / les données des membres et des fonctions membres pour CPrintEditObj aller ici

/ / Cartes d'interface
protégé :
    DECLARE_I&NTERFACE_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 raconte le cadre que cette classe auront une carte d'interface personnalisée. En outre, les macros BEGIN_INTERFACE_PART et END_INTERFACE_PART définissent les classes imbriquées, dans ce cas avec les noms CEditObj et CPrintObj (le x est utilisé uniquement pour différencier les classes imbriquées globales classes qui commencent par "C" et les classes d'interface qui commencent par "I"). Membres imbriqués deux de ces classes sont créées : m_CEditObj et m_CPrintObj, respectivement. Les macros automatiquement déclarent la AddRefet Releasefonctions QueryInterface ; donc vous seulement déclarez les fonctions spécifiques à cette interface : EditObject et PrintObject (la macro OLE STDMETHOD est tel utilisée afin que _stdcall et mots clés virtuelles sont fournis comme approprié pour la plate-forme cible).

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

BEGI&N_INTERFACE_MAP (CPrintEditObj, CCmdTarget)
 nbsp ;  INTERFACE_PART (CPrintEditObj, IID_IPrintInterface, PrintObj)
    INTERFACE_PART (CPrintEditObj, IID_IEditInterface, EditObj)
END_INTERFACE_MAP()

Il relie l'IID IID_IPrintInterface avec m_CPrintObj et IID_IEditInterface m_CEditObj respectivement. La mise en œuvre de CCmdTarget de QueryInterface (CCmdTarget::ExternalQueryInterface) utilise cette carte pour retourner des pointeurs à m_CPrintObj et m_CEditObj lorsque demandé. Il n'est pas nécessaire d'inclure une entrée pour IID_IUnknown; le cadre utilisera la première interface de la carte (dans ce cas, m_CPrintObj) quand IID_IUnknown est demandée.

Même si la macro BEGIN_INTERFACE_PART déclarée automatiquement la AddRefet Release fonctions QueryInterface pour vous, vous devez toujours mettre:

ULONG lointain EXPORT CEditPrintObj::XEditObj::AddRef()
{
 nbsp ;  METHOD_PROLOGUE (CEditPrintObj, EditObj)
    retour pThis - > ExternalAddRef() ;
}

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

HRESULT loin d'exportation () CEditPrintObj::XEditObj::QueryInterface
    REFIID iid, void FAR * FAR * ppvObj)
{
    METHOD_PROLOGUE (CEditPrintObj, EditObj)
    retour (HRESULT) pThis - > ExternalQueryInterface (& iid, ppvObj) ;
}

VOID CEditPrintObj::XEditObj::EditObject() beaucoup à l'exportation
{
    METHOD_PROLOGUE (CEditPrintObj, EditObj)
    / / code pour « Edit » l'objet, quel que soit ce moyen...
}

La mise en œuvre de CEditPrintObj::CPrintObj, serait semblable aux définitions ci-dessus pour les CEditPrintObj::CEditObj. Bien qu'il serait possible de créer une macro qui pourrait être utilisée pour générer automatiquement ces fonctions (en fait, plus tôt dans le développement de MFC/OLE c'était le cas), il devient difficile d'établir des points de rupture lorsqu'une macro génère plus d'une ligne de code. Pour cette raison, ce code est développé manuellement.

À l'aide de la mise en œuvre du cadre du message de cartes y sont un certain nombre de choses qui n'étaient pas nécessaire de le faire:

En outre, le framework utilise cartes message en interne. Cela permet de dériver une classe de cadre, dire COleServerDoc, qui déjà prend en charge certaines interfaces et fournit des remplacements ou des ajouts aux interfaces fournis par le framework. Cette option est activée par le fait que le cadre soutient pleinement l'héritage une interface carte d'une classe de base — c'est la raison pourquoi BEGIN_INTERFACE_MAP prend comme deuxième paramètre le nom de la classe de base.

&Notenbsp ;  Il n'est généralement pas possible de réutiliser la mise en œuvre des implémentations intégrées de MFC des interfaces OLE en héritant de la spécialisation embarquée de l'interface de la version MFC. Ce n'est pas possible parce que l'utilisation de la macro METHOD_PROLOGUE pour accéder à la contenant CCmdTarget-objet dérivé implique un fixe l'offset de l'objet incorporé de la CCmdTarget-objet dérivé. Cela signifie, par exemple, que vous ne peut pas dériver un XMyAdviseSink intégré de l'application MFC dans COleClientItem::XAdviseSink, car XAdviseSink s'appuie sur un décalage spécifique du haut de l'objet COleClientItem étant.

Vous pouvez, toutefois, déléguer à l'implémentation MFC pour toutes les fonctions que vous voulez le comportement par défaut MFC. Cela se fait dans l'application MFC IOleInPlaceFrame (XOleInPlaceFrame) dans la classe COleFrameHook (elle délègue à la m_xOleInPlaceUIWindow de nombreuses fonctions). Cette conception a été choisie pour réduire la taille du runtime des objets qui implémentent les interfaces beaucoup ; Il élimine la nécessité d'un retour de pointeur (comme le m_pParent de la façon dont a été utilisé dans la section précédente).

Agrégation et cartes d'Interface

En plus de soutenir les objets COM autonomes, MFC prend également en charge l'agrégation. Agrégation elle-même est un sujet trop complexe pour discuter ici ; reportez-vous à la référence du programmeur OLE de pour plus d'informations sur l'agrégation. Cette note décrit simplement le soutien pour l'agrégation, construit dans les cartes d'interface et de cadre.

Il existe deux façons d'utiliser l'agrégation: (1) à l'aide d'un objet COM qui prend en charge l'agrégation et (2) mise en œuvre d'un objet qui peut être agrégé par une autre personne. Ces capacités peuvent être appelées « à l'aide d'un objet aggrégé » et « rendre un objet agrégeables ». MFC prend en charge les deux.

À l'aide d'un objet aggrégé

Pour utiliser un objet aggrégé, doit être une façon de lier l'agrégat dans le mécanisme de QueryInterface. En d'autres termes, l'objet global doit se comporter comme si elle fait partie de votre objet native. Alors comment cette cravate au mécanisme de carte interface MFC ? En plus de la macro INTERFACE_PART , où un objet imbriqué est mappé à un IID, vous pouvez également déclarer un objet aggrégé dans le cadre de votre classe de CCmdTarget dérivé. Pour ce faire, la macro INTERFACE_AGGREGATE est utilisée. Cela vous permet de spécifier une variable de membre (qui doit être un pointeur vers un IUnknown ou classe dérivée), qui doit être intégré dans le mécanisme de la carte interface. Si le pointeur n'est pas NULL lorsque CCmdTarget::ExternalQueryInterface est appelé, le cadre sera appelle automatiquement la fonction de membre de QueryInterface de l'objet global, si l' IID demandé n'est pas un s IIDnatif pris en charge par l'objet de CCmdTarget lui-même.

Pour utiliser la macro INTERFACE_AGGREGATE, procédez comme suit:

  1. Déclarez une variable de membre (un IUnknown *) qui contient un pointeur vers l'objet agrégat.

  2. Inclure une macro INTERFACE_AGGREGATE de votre carte d'interface, qui fait référence à la variable de membre par nom.

  3. À un moment donné (habituellement au cours de CCmdTarget::OnCreateAggregates), initialiser la variable membre à autre chose que NULL.

Par exemple,

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

protégé :
    LPUNKNOWN m_lpAggrInner ;
    Virtual BOOL OnCreateAggregates() ;

DECLARE_INTERFACE_MAP()
    / / macros de partie interface « native » peuvent être utilisées ici
};

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

BOOL CAggrExample::OnCreateAggregates()
{
    / / fil des granulats avec inconnu contrôle correct
    m_lpAggrInner = CoCreateInstance (CLSID_Example,
        GetControllingUnknown(), CLSCTX_INPROC_SERVER,
        IID_IUnknown, (LPVOID **) & m_lpAggrInner) ;
    Si (m_lpAggrInner == NULL)
        Return FALSE ;
    et éventuellement, créer un autres agrégats objets ici
    Return TRUE ;
}

BEGIN_INTERFACE_MAP (CAggrExample, CCmdTarget)
    / / entrées originaire de « INTERFACE_PART » aller ici
    INTERFACE_AGGREGATE (CAggrExample, m_lpAggrInner)
END_INTERFACE_MAP()

Le m_lpAggrInner est initialisé dans le constructeur à NULL. Le cadre d'ignorer une variable de membre NULL dans l'implémentation par défaut de QueryInterface. OnCreateAggregates est un bon endroit pour créer réellement des objets ensemble. Vous devrez appeler explicitement si vous créez l'objet à l'extérieur de l'application MFC de COleObjectFactory. La raison pour la création d'agrégats en CCmdTarget::OnCreateAggregates ainsi que l'utilisation de CCmdTarget::GetControllingUnknown deviendra évidente lors de la création d'objets agrégées est discutée.

Cette technique donnera votre objet de toutes les interfaces de l'objet global prend plus ses interfaces natives. Si vous souhaitez uniquement un sous-ensemble des interfaces qui prend en charge l'agrégat, vous pouvez substituer CCmdTarget::GetInterfaceHook. Cela vous permet de hookability niveau très faible, similaire à QueryInterface. Généralement, vous souhaitez que toutes les interfaces qui prend en charge l'ensemble.

Faire une mise en œuvre de l'objet agrégeables

Pour un objet à être agrégées, la mise en œuvre de QueryInterface , AddRefet Releasedoit déléguer à un « contrôle inconnu. » En d'autres termes, pour elle faire partie de l'objet, il doit déléguer QueryInterface , AddRefet Releaseun objet différent, également dérivée de IUnknown. Cette « commande inconnue » est fourni à l'objet lorsqu'il est créé, qui est, il est fourni à la mise en œuvre de COleObjectFactory. Mise en œuvre de cette porte une petite quantité de frais généraux et dans certains cas n'est pas souhaitable, pour MFC rend facultatif. Pour activer un objet à être agrégées, vous appelez CCmdTarget::EnableAggregation du constructeur de l'objet.

Si l'objet utilise également des agrégats, vous devez être sûr de passer le bon « commande inconnue » aux objets globales. Habituellement, ce pointeur IUnknown est transmis à l'objet lorsque l'ensemble est créé. Par exemple, le paramètre pUnkOuter est la « commande inconnu » pour les objets créés avec la fonction CoCreateInstance. Le pointeur correct « contrôle inconnu » peut être récupéré en appelant CCmdTarget::GetControllingUnknown. La valeur retournée par cette fonction, toutefois, ne valide pendant le constructeur. Pour cette raison, il est suggéré que vous créez vos agrégats seulement dans une substitution de CCmdTarget::OnCreateAggregates, où la valeur de retour de GetControllingUnknown est fiable, même si créé à partir de la mise en œuvre de la COleObjectFactory.

Il est aussi important que l'objet manipuler le décompte de références correctes lorsque ajoutant ou en libérant des comtes de référence artificiel. Pour s'assurer que c'est le cas, appelez toujours ExternalAddRef 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

Utilisation avancée de OLE, tels que la définition de vos propres interfaces ou substitution du mise en œuvre du cadre des interfaces OLE requiert l'utilisation du mécanisme sous-jacent de carte interface.

Cette section traite chaque macro et l'API qui sont utilisés pour implémenter ces fonctionnalités avancées.

CCmdTarget::EnableAggregation — Fonction Description

void EnableAggregation() ;

&Notenbsp ;  Appelez cette fonction dans le constructeur de la classe dérivée si vous souhaitez soutenir l'agrégation OLE pour les objets de ce type. Cela prépare une mise en œuvre IUnknown spécial est requis pour les objets agrégeables.

CCmdTarget::ExternalQueryInterface — Fonction Description

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

lpIID

Un pointeur far vers un IID (le premier argument de QueryInterface)

ppvObj

Un pointeur IUnknown à un (second argument de QueryInterface)

&Notenbsp ;  Appelez cette fonction dans votre implémentation de IUnknown pour chaque interface de votre classe implémente. Cette fonction fournit l'implémentation de piloté par des données standard de QueryInterface basé sur la carte d'interface de votre objet. Il est nécessaire de jeter la valeur de retour HRESULT. Si l'objet est agrégée, cette fonction appelle la « contrôlant IUnknown » au lieu d'utiliser la carte d'interface locale.

CCmdTarget::ExternalAddRef — Fonction Description

DWORD ExternalAddRef() ;

&Notenbsp ;  Appelez cette fonction dans votre implémentation de IUnknown::AddRef pour chaque interface de votre classe implémente. La valeur de retour est le nouveau comte de référence sur l'objet de CCmdTarget. Si l'objet est agrégée, cette fonction appelle la « contrôlant IUnknown » au lieu de manipuler le décompte de références locales.

CCmdTarget::ExternalRelease — Fonction Description

DWORD ExternalRelease();

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

DECLARE_INTERFACE_MAP — Description de Macro

DECLARE_INTERFACE_MAP

&Notenbsp ;  Utilisez cette macro dans toute classe dérivée de CCmdTarget qui auront un mappage d'interface. Utilisé à peu près la même façon que DECLARE_MESSAGE_MAP. Invocation de cette macro doit être placée dans la définition de classe, habituellement dans un en-tête (.H) Numéro de dossier. Une classe avec DECLARE_INTERFACE_MAP doit définir le mappage d'interface dans le fichier de mise en œuvre (.RPC) avec les macros BEGIN_INTERFACE_MAP et END_INTERFACE_MAP.

BEGIN_INTERFACE_PART et END_INTERFACE_PART — Descriptions Macro

BEGIN_INTERFACE_PART (localClass, iface);

END_INTERFACE_PART (localClass)

localClass

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

iface

Le nom de cette classe implémente l'interface

&Notenbsp ;  Pour chaque interface que votre classe mettra en œuvre, vous devez avoir 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 comme une variable de membre incorporé de cette catégorie. Les membres de QueryInterface , AddRefet Releasesont automatiquement déclarées. Vous devez inclure les déclarations pour les autres fonctions membres qui font partie de l'interface de mise en œuvre (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 mettre en œuvre, tels IAdviseSink, ou IPersistStorage (ou votre propre interface personnalisée).

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

Par exemple:

BEGI&N_INTERFACE_PART (MyAdviseSink, IAdviseSink)
 nbsp ; STDMETHOD_(void,OnDataChange) (LPFORMATETC, LPSTGMEDIUM) ;
   STDMETHOD_(void,OnViewChange) (DWORD, longue) ;
   STDMETHOD_(void,OnRename)(LPMONIKER) ;
   STDMETHOD_(void,OnSave)() ;
   STDMETHOD_(void,OnClose)() ;
END_INTERFACE_PART(MyAdviseSink)

serait de définir une classe locale appelée XMyAdviseSink, dérivé de IAdviseSink, et un membre de la classe dans laquelle il est déclaré appelé m_xMyAdviseSink.Note:

&Notenbsp ;  Les lignes commençant par STDMETHOD_ sont essentiellement copiés de OLE2.H et légèrement modifiée. Copie à partir de OLE2.H peut réduire les erreurs qui sont difficiles à résoudre.

BEGIN_INTERFACE_MAP et END_INTERFACE_MAP — Descriptions Macro

BEGIN_INTERFACE_MAP (theClass, baseClass)

END_INTERFACE_MAP

theClass

La classe dans laquelle la carte d'interface doit être définie

baseClass

La classe dont dérive la theClass de.

Remarques : Les macros BEGIN_INTERFACE_MAP et END_INTERFACE_MAP sont utilisés dans le fichier de mise en œuvre de réellement définir le mappage d'interface. Pour chaque interface est mis en place il y a un ou plusieurs appels de macro INTERFACE_PART . Pour chaque agrégat qui utilise la classe, il y a une invocation de macro INTERFACE_AGGREGATE.

INTERFACE_PART — Description de Macro

INTERFACE_PART (theClass, iid, localClass)

theClass

Le nom de la classe qui contient le mappage d'interface.

iid

L' IID , qui doit être mappée à la classe intégrée.

localClass

Le nom de la classe locale (moins de « X »)

&Notenbsp ;  Cette macro est utilisée entre la macro BEGIN_INTERFACE_MAP et la macro END_INTERFACE_MAP pour chaque interface que soutiendra votre objet. Il vous permet de mapper un IID à un membre de la classe indiquée par theClass et localClass. Le « m_x » seront ajoutés automatiquement à la localClass . Notez que plus d'un IID peut être associée à un seul membre. Cela est très utile lorsque vous mettent en œuvre uniquement une interface « plus dérivée » et souhaitez fournir toutes les interfaces intermédiaires ainsi. Un bon exemple de cela est l'interface IOleInPlaceFrameWindow . Sa hiérarchie ressemble à ceci:

IU&nknown
 nbsp ;  IOleWindow
        IOleUIWindow
            IOleInPlaceFrameWindow

Si un objet implémente IOleInPlaceFrameWindow, un client peut QueryInterface sur l'une de ces interfaces : IOleUIWindow, IOleWindowou IUnknown, outre l'interface « plus dérivée » IOleInPlaceFrameWindow (celle qui vous mettent réellement en œuvre). Pour gérer cela, vous pouvez utiliser plus d'un INTERFACE_PART macro pour mapper chaque interface de base pour l'interface IOleInPlaceFrameWindow

dans le fichier de définition de classe:

BEGIN_INTERFACE_PART (CMyFrameWindow, IOleInPlaceFrameWindow)

dans le fichier de mise en œuvre de classe:

BEGI&N_INTERFACE_MAP (CMyWnd, CFrameWnd)
 nbsp ;  INTERFACE_PART (CMyWnd, IID_IOleWindow, MyFrameWindow)
    INTERFACE_PART (CMyWnd, IID_IOleUIWindow, MyFrameWindow)
    INTERFACE_PART (CMyWnd, IID_IOleInPlaceFrameWindow, MyFrameWindow)
END_INTERFACE_MAP

Le cadre prend soin de IUnknown, puisqu'il est toujours nécessaire.

INTERFACE_PART — Description de Macro

INTERFACE_AGGREGATE (theClass, theAggr)

theClass

Le nom de la classe qui contient le mappage d'interface,

theAggr

Le nom de la variable de membre qui doit être agrégés.

&Notenbsp ;  Cette macro est utilisée pour indiquer le cadre que la classe est à l'aide d'un objet global. Il doit être clair entre les macros BEGIN_INTERFACE_PART et END_INTERFACE_PART . Un objet aggrégé est un objet distinct, dérivé de IUnknown. En utilisant un agrégat et la macro INTERFACE_AGGREGATE , vous pouvez faire toutes les interfaces qui le prend en charge globales semble être directement pris en charge par l'objet. L'argument theAggr est tout simplement le nom d'une variable de membre de votre classe qui est dérivée de IUnknown (directement ou indirectement). Toutes les macros INTERFACE_AGGREGATE doivent suivre les macros INTERFACE_PART lorsqu'ils sont placés dans un mappage d'interface.

&Notes techniques par le numéro |nbsp ; Notes techniques par catégorie

Index