Partager via


TN065 : Prise en charge d'interfaces doubles pour les serveurs d'OLE Automation

[!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.

Cette remarque explique comment ajouter la prise en charge d'interfaces doubles à une application serveur basée sur MFC OLE Automation.L'exemple d' ACDUAL illustre la prise en charge d'interfaces doubles, et l'exemple de code dans cette remarque est pris d'ACDUAL.Les macros décrites dans cette remarque, telle qu' DECLARE_DUAL_ERRORINFO, DUAL_ERRORINFO_PART, et IMPLEMENT_DUAL_ERRORINFO, font partie de l'exemple d'ACDUAL et se trouvent dans MFCDUAL.H.

Interfaces doubles

Bien que OLE Automation vous permette d'implémenter une interface d' IDispatch , une interface VTBL, ou une interface double (qui entoure les deux), Microsoft fortement recommandé d'implémenter des interfaces doubles pour tous les objets exposés OLE Automation.Les interfaces doubles ont des avantages significatifs par rapport à les interfaces réservées ou réservées VTBL d' IDispatch:

  • La liaison peut avoir lieu au moment de la compilation via l'interface VTBL, ou au moment de l'exécution via IDispatch.

  • Les contrôleurs OLE Automation qui peuvent utiliser l'interface VTBL peuvent bénéficier des performances.

  • Les contrôleurs OLE Automation existants qui utilisent l'interface d' IDispatch continueront à fonctionner.

  • Il est plus facile appeler l'interface VTBL C++.

  • Les interfaces doubles sont obligatoires pour la compatibilité avec les fonctionnalités de prise en charge d'objet Visual Basic.

Prise en charge d'interfaces doubles à une classe CCmdTarget-Basée

Une interface double est uniquement une interface personnalisée dérivée d' IDispatch.La façon la plus simple d'implémenter la prise en charge d'interfaces doubles dans une classe basée sur d' CCmdTargetest à la première implémentent l'interface de dispatch normale sur votre classe à l'aide de MFC et assistant classe, puis ajoutez l'interface personnalisée ultérieurement.Pour la plupart, votre implémentation personnalisée d'interface délèguera simplement dans MFC IDispatch l'implémentation.

D'abord, modifiez le fichier ODL de votre serveur définisse les interfaces doubles pour vos objets.Pour définir une interface double, vous devez utiliser une instruction d'interface, au lieu de l'instruction d' DISPINTERFACE que les assistants Visual C++ génèrent.Plutôt qu'en supprimant l'instruction existante d' DISPINTERFACE , ajoutez une nouvelle instruction d'interface.En conservant le formulaire d' DISPINTERFACE , vous pouvez continuer à utiliser l'assistant classe pour ajouter des propriétés et des méthodes à votre objet, mais vous devez ajouter des propriétés et des méthodes équivalentes à votre instruction d'interface.

Une instruction d'interface pour une interface double doit avoir les attributs d' OLEAUTOMATION et de DUAL , et l'interface doit être dérivée d' IDispatch.Vous pouvez utiliser l'exemple de GUIDGEN pour créer IID pour l'interface double :

[ uuid(0BDD0E81-0DD7-11cf-BBA8-444553540000), // IID_IDualAClick
   oleautomation,
   dual
]
interface IDualAClick : IDispatch
  {
  };

Une fois que vous avez l'instruction d'interface, démarrez les entrées d'ajout de les méthodes et les propriétés.Pour les interfaces doubles, vous devez organiser les listes de paramètres afin que vos méthodes et fonctions d'accesseur de propriété dans l'interface double retournent HRESULT et passez leurs valeurs de retour comme paramètres avec les attributs [retval,out].Souvenez -vous que pour les propriétés, vous devrez ajouter une lecture (propget) et la fonction d'accès d'écriture (propput) avec le même ID.Par exemple :

[propput, id(1)] HRESULT text([in] BSTR newText);
[propget, id(1)] HRESULT text([out, retval] BSTR* retval);

Après vos méthodes et propriétés sont définis, vous devez ajouter une référence à l'instruction d'interface dans l'instruction de la coclasse.Par exemple :

[ uuid(4B115281-32F0-11cf-AC85-444553540000) ]
coclass Document
{
   dispinterface IAClick;
   [default] interface IDualAClick;
};

Une fois que votre fichier ODL a été mis à jour, utilisez le mécanisme de mappage d'interfaces MFC pour définir une classe d'implémentation de l'interface double dans votre classe d'objets et pour effectuer les entrées correspondantes dans le mécanisme d' QueryInterface MFC.Vous avez besoin d'une entrée dans le bloc d' INTERFACE_PART pour chaque entrée dans l'instruction d'interface ODL, plus les entrées d'une interface de dispatch.Chaque entrée ODL avec l'attribut de propput a besoin d'une fonction nommée put_propertyname.Chaque entrée avec l'attribut de propget a besoin d'une fonction nommée get_propertyname.

Pour définir une classe d'implémentation pour votre interface double, ajoutez un bloc d' DUAL_INTERFACE_PART à la définition de classe d'objets.Par exemple :

BEGIN_DUAL_INTERFACE_PART(DualAClick, IDualAClick)
  STDMETHOD(put_text)(THIS_ BSTR newText);
  STDMETHOD(get_text)(THIS_ BSTR FAR* retval);
  STDMETHOD(put_x)(THIS_ short newX);
  STDMETHOD(get_x)(THIS_ short FAR* retval);
  STDMETHOD(put_y)(THIS_ short newY);
  STDMETHOD(get_y)(THIS_ short FAR* retval);
  STDMETHOD(put_Position)(THIS_ IDualAutoClickPoint FAR* newPosition);
  STDMETHOD(get_Position)(THIS_ IDualAutoClickPoint FAR* FAR* retval);
  STDMETHOD(RefreshWindow)(THIS);
  STDMETHOD(SetAllProps)(THIS_ short x, short y, BSTR text);
  STDMETHOD(ShowWindow)(THIS);
END_DUAL_INTERFACE_PART(DualAClick)

Pour connecter l'interface double au mécanisme d' Appeler QueryInterface MFC, ajoutez une entrée d' INTERFACE_PART au mappage d'interfaces :

BEGIN_INTERFACE_MAP(CAutoClickDoc, CDocument)
  INTERFACE_PART(CAutoClickDoc, DIID_IAClick, Dispatch)
  INTERFACE_PART(CAutoClickDoc, IID_IDualAClick, DualAClick)
END_INTERFACE_MAP()

Ensuite, vous devez effectuer l'implémentation de l'interface.Pour la plupart, vous pouvez déléguer aux MFC IDispatch l'implémentation existante.Par exemple :

STDMETHODIMP_(ULONG) CAutoClickDoc::XDualAClick::AddRef()
{
   METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
   return pThis->ExternalAddRef();
}
STDMETHODIMP_(ULONG) CAutoClickDoc::XDualAClick::Release()
{
   METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
   return pThis->ExternalRelease();
}
STDMETHODIMP CAutoClickDoc::XDualAClick::QueryInterface(
             REFIID iid, LPVOID* ppvObj)
{
   METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
   return pThis->ExternalQueryInterface(&iid, ppvObj);
}
STDMETHODIMP CAutoClickDoc::XDualAClick::GetTypeInfoCount(
            UINT FAR* pctinfo)
{
   METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
   LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
   ASSERT(lpDispatch != NULL);
   return lpDispatch->GetTypeInfoCount(pctinfo);
}
STDMETHODIMP CAutoClickDoc::XDualAClick::GetTypeInfo(
          UINT itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
{
   METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
   LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
   ASSERT(lpDispatch != NULL);
   return lpDispatch->GetTypeInfo(itinfo, lcid, pptinfo);
}
STDMETHODIMP CAutoClickDoc::XDualAClick::GetIDsOfNames(
       REFIID riid, OLECHAR FAR* FAR* rgszNames, UINT cNames,
       LCID lcid, DISPID FAR* rgdispid) 
{
   METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
   LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
   ASSERT(lpDispatch != NULL);
   return lpDispatch->GetIDsOfNames(riid, rgszNames, cNames, 
                                    lcid, rgdispid);
}
STDMETHODIMP CAutoClickDoc::XDualAClick::Invoke(
    DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
    DISPPARAMS FAR* pdispparams, VARIANT FAR* pvarResult,
    EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr)
{
   METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
   LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
   ASSERT(lpDispatch != NULL);
   return lpDispatch->Invoke(dispidMember, riid, lcid,
                             wFlags, pdispparams, pvarResult,
                             pexcepinfo, puArgErr);
}

Pour vos méthodes de l'objet et fonctions d'accesseur de propriété, vous devez réaliser l'implémentation.Les fonctions de méthode et de propriété peuvent généralement les déléguer à nouveau aux méthodes générées à l'aide de l'assistant classe.Toutefois, si vous installez des propriétés pour accéder à des variables directement, vous devez écrire le code pour obtenir/placez la valeur dans la variable.Par exemple :

STDMETHODIMP CAutoClickDoc::XDualAClick::put_text(BSTR newText)
{
   METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
   // MFC automatically converts from Unicode BSTR to 
   // Ansi CString, if necessary...
   pThis->m_str = newText;
   return NOERROR;
}
STDMETHODIMP CAutoClickDoc::XDualAClick::get_text(BSTR* retval)
{
   METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
   // MFC automatically converts from Ansi CString to 
   // Unicode BSTR, if necessary...
   pThis->m_str.SetSysString(retval);
   return NOERROR;
}

Passage des pointeurs d'interface double

Passer votre pointeur d'interface double n'est pas simple, surtout si vous devez appeler CCmdTarget::FromIDispatch.FromIDispatch fonctionne uniquement aux pointeurs d' IDispatch MFC.Une façon de contourner ce problème consiste à rechercher l'installation du pointeur d' IDispatch d'origine par les MFC et de passer ce pointeur vers les fonctions qui en ont besoin.Par exemple :

STDMETHODIMP CAutoClickDoc::XDualAClick::put_Position(
      IDualAutoClickPoint FAR* newPosition)
{
   METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
   LPDISPATCH lpDisp = NULL;
   newPosition->QueryInterface(IID_IDispatch, (LPVOID*)&lpDisp);
   pThis->SetPosition(lpDisp);
   lpDisp->Release();
   return NOERROR;
}

Avant de passer un arrière de pointeur dans la méthode de son interface double, vous devrez peut-être la conversion du pointeur MFC IDispatch à votre pointeur d'interface double.Par exemple :

STDMETHODIMP CAutoClickDoc::XDualAClick::get_Position(
      IDualAutoClickPoint FAR* FAR* retval)
{
   METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
   LPDISPATCH lpDisp;
   lpDisp = pThis->GetPosition();
   lpDisp->QueryInterface(IID_IDualAutoClickPoint, (LPVOID*)retval);
   return NOERROR;
}

Inscrire la bibliothèque de types de l'application

AppWizard ne génère pas de code pour inscrire la bibliothèque de types d'une application serveur OLE Automation avec le système.Bien qu'il existe d'autres méthodes pour inscrire la bibliothèque de types, il est utile d'avoir le registre d'application de la bibliothèque de types lorsqu'il met à jour ses OLE informations de type, c. autrement dit., chaque fois que l'application est exécutée de manière autonome.

Pour inscrire la bibliothèque de types de l'application chaque fois que l'application est exécutée de manière autonome :

  • Incluez AFXCTL.H dans votre standard inclut le fichier d'en-tête, STDAFX.H, pour accéder à la définition de la fonction d' AfxOleRegisterTypeLib .

  • Dans la fonction d' InitInstance de votre application, localisez l'appel à COleObjectFactory::UpdateRegistryAll.Après cet appel, ajoutez un appel à AfxOleRegisterTypeLib, en spécifiant LIBID correspondant à votre bibliothèque de types, avec le nom de votre bibliothèque de types :

    // When a server application is launched stand-alone, it is a good idea
    // to update the system registry in case it has been damaged.
    m_server.UpdateRegistry(OAT_DISPATCH_OBJECT);
    COleObjectFactory::UpdateRegistryAll();
    // DUAL_SUPPORT_START
    // Make sure the type library is registered or dual interface won't work.
    AfxOleRegisterTypeLib(AfxGetInstanceHandle(), LIBID_ACDual, _T("AutoClik.TLB"));
    // DUAL_SUPPORT_END
    

Modification des paramètres de génération de projet pour s'adapter aux modifications de bibliothèque de types

Pour modifier les paramètres de la génération d'un projet afin qu'un fichier d'en-tête contenant des définitions d' UUID soit généré par MkTypLib chaque fois que la bibliothèque de types est régénérée :

  1. Dans le menu Générer , cliquez sur Paramètres, puis sélectionnez le fichier ODL de la liste des fichiers pour chaque configuration.

  2. Cliquez sur l'onglet d' OLE Types et spécifiez un nom de fichier dans le champ de nom de fichier d' Output header .Utilisez un nom de fichier qui n'est pas déjà utilisé par votre projet, car MkTypLib remplace les fichiers existants.Cliquez sur OK pour fermer la boîte de dialogue de Build Settings .

Pour ajouter les définitions d' UUID à partir de le fichier d'en-tête MkTypLib-généré à votre projet :

  1. Ajoutez le fichier d'en-tête MkTypLib-généré dans votre standard inclut le fichier d'en-tête, STDAFX.H.

  2. Créez un nouveau fichier, INITIIDS.CPP, et ajoutez-le à votre projet.Dans ce fichier, incluez le fichier d'en-tête MkTypLib-généré après l'ajout de OLE2.H et INITGUID.H :

    // initIIDs.c: defines IIDs for dual interfaces
    // This must not be built with precompiled header.
      #include <ole2.h>
      #include <initguid.h>
      #include "acdual.h"
    
  3. Dans le menu Générer , cliquez sur Paramètres, puis sélectionnez INITIIDS.CPP de la liste des fichiers pour chaque configuration.

  4. Cliquez sur l'onglet de C++ , cliquez sur la catégorie Precompiled Headers, et sélectionnez la case d'option de Not using precompiled headers .Cliquez sur OK pour fermer la boîte de dialogue de Build Settings .

Spécifier le nom de la classe d'objets correct dans la bibliothèque de types

Les assistants livrés avec Visual C++ utilisent correctement le nom de classe d'implémentation pour spécifier la coclasse du fichier ODL de le serveur pour les classes d'OLE-creatable.Bien que cela fonctionne, le nom de classe d'implémentation n'est probablement pas le nom de classe que vous souhaitez que les utilisateurs de votre objet à utiliser.Pour spécifier le nom correct, ouvrez le fichier ODL, trouver chaque instruction de la coclasse, et remplacer le nom de classe d'implémentation par le nom externe correct.

Notez que lorsque l'instruction de la coclasse est modifiée, les noms de variables de CLSIDs dans le fichier d'en-tête MkTypLib-généré changent en conséquence.Vous devrez mettre à jour votre code pour utiliser les nouveaux noms de variables.

Gestion des exceptions et les interfaces d'erreur automation

Les méthodes de votre objet Automation et fonctions d'accesseur de propriété peuvent lever des exceptions.Dans ce cas, vous devez les gérer dans votre implémentation de l'interface double et passer des informations sur l'exception est retournée au contrôleur via l'interface de gestion des erreurs OLE Automation, IErrorInfo.Cette interface fournit des informations sur les erreurs détaillées et contextuelles via IDispatch et des interfaces VTBL.Pour indiquer qu'un gestionnaire d'erreurs est disponible, vous devez implémenter l'interface d' ISupportErrorInfo .

Pour illustrer le mécanisme de gestion des erreurs, supposez que les fonctions ClassWizard-générées utilisées pour implémenter les exceptions standard throw de prise en charge d'expédition.L'implémentation MFC d' IDispatch::Invoke généralement intercepte les exceptions et les convertit en une structure d'EXCEPTINFO qui est retournée par l'appel d' Invoke .Toutefois, lorsque l'interface VTBL est utilisée, vous êtes chargé d'intercepter les exceptions vous-même.À titre de exemple protéger vos méthodes d'interface double :

STDMETHODIMP CAutoClickDoc::XDualAClick::put_text(BSTR newText)
{
   METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
   TRY_DUAL(IID_IDualAClick)
   {
      // MFC automatically converts from Unicode BSTR to 
      // Ansi CString, if necessary...
      pThis->m_str = newText;
      return NOERROR;
   }
   CATCH_ALL_DUAL
}

CATCH_ALL_DUAL se charge de retourner un code d'erreur approprié lorsqu'une exception se produit.CATCH_ALL_DUAL convertit une exception MFC dans des informations de gestion des erreurs OLE Automation à l'aide de l'interface d' ICreateErrorInfo .(Une macro d' CATCH_ALL_DUAL d'exemple est dans le fichier MFCDUAL.H dans l'exemple d' ACDUAL .La fonction appelées pour gérer les exceptions, DualHandleException, dans le fichier MFCDUAL.CPP.) CATCH_ALL_DUAL détermine le code d'erreur pour retourner selon le type d'exception qui s'est produite :

  • COleDispatchException – dans ce cas, HRESULT est généré en utilisant le code suivant :

    hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 
                               (e->m_wCode + 0x200));
    

    Cela crée un détail d' HRESULT à l'interface qui a provoqué l'exception.Code d'erreur est offset par 0x200 pour éviter tout conflit avec HRESULTdéfini par le système s pour de interfaces OLE standard.

  • CMemoryException – dans ce cas, E_OUTOFMEMORY est retourné.

  • Une autre exception (dans ce cas, E_UNEXPECTED est retourné.

Pour indiquer que le gestionnaire d'erreurs OLE Automation est utilisé, vous devez également implémenter l'interface de ISupportErrorInfo .

Tout d'abord, ajoutez du code à votre définition de classe automation pour l'afficher que prend en charge ISupportErrorInfo.

Ensuite, ajoutez le code au mappage d'interfaces de votre classe d'automation pour associer la classe d'implémentation d' ISupportErrorInfo avec le mécanisme d' QueryInterface MFC.l'instruction d' INTERFACE_PART correspond à la classe définie pour ISupportErrorInfo.

Enfin, implémentez la classe définie pour prendre en charge ISupportErrorInfo.

(L'exemple d' ACDUAL contient trois macros pour aider à effectuer ces trois étapes, DECLARE_DUAL_ERRORINFO, DUAL_ERRORINFO_PART, et IMPLEMENT_DUAL_ERRORINFO, tout contenu dans MFCDUAL.H.)

l'exemple suivant implémente une classe définie pour prendre en charge ISupportErrorInfo.CAutoClickDoc est le nom de votre classe d'automation et IID_IDualAClick est IID pour l'interface qui est la source des erreurs signalées par le biais de l'objet d'erreur OLE Automation :

STDMETHODIMP_(ULONG) CAutoClickDoc::XSupportErrorInfo::AddRef() 
{
   METHOD_PROLOGUE(CAutoClickDoc, SupportErrorInfo) 
   return pThis->ExternalAddRef(); 
} 
STDMETHODIMP_(ULONG) CAutoClickDoc::XSupportErrorInfo::Release() 
{ 
   METHOD_PROLOGUE(CAutoClickDoc, SupportErrorInfo) 
   return pThis->ExternalRelease(); 
} 
STDMETHODIMP CAutoClickDoc::XSupportErrorInfo::QueryInterface( 
   REFIID iid, LPVOID* ppvObj) 
{ 
   METHOD_PROLOGUE(CAutoClickDoc, SupportErrorInfo) 
   return pThis->ExternalQueryInterface(&iid, ppvObj); 
} 
STDMETHODIMP CAutoClickDoc::XSupportErrorInfo::InterfaceSupportsErrorInfo( 
   REFIID iid) 
{ 
   METHOD_PROLOGUE(CAutoClickDoc, SupportErrorInfo) 
   return (iid == IID_IDualAClick) ? S_OK : S_FALSE; 
}

Voir aussi

Autres ressources

Notes techniques de nombres

Notes techniques de catégorie