TN065 : Interface double Support des serveurs OLE Automation

Cette note explique comment ajouter le support des interfaces doubles à une application de serveur de base de MFC OLE Automation. La exemple ACDUAL illustre l'interface double appui, et l'exemple de code dans cette note est tirée de ACDUAL. Les macros décrites dans cette note, comme DECLARE_DUAL_ERRORINFO , DUAL_ERRORINFO_PART , et IMPLEMENT_DUAL_ERRORINFO , font partie de l'exemple ACDUAL et se trouvent dans MFCDUAL.H.

Interfaces doubles

Bien que OLE Automation vous permet d'implémenter une interface IDispatch , une interface VTBL ou une interface double (qui englobe les deux), Microsoft recommande fortement que vous implémentez des interfaces doubles pour tous les objets exposés de OLE Automation. Interfaces doubles ont des avantages significatifs sur IDispatch-seules ou VTBL interfaces:

Ajouter le Support de l'Interface double dans une classe de base de CCmdTarget

Une interface double est vraiment juste une interface personnalisée dérivée de IDispatch. La voie la plus directe pour mettre en œuvre d'interface double appui dans un CCmdTarget-classe de base est à mettre en oeuvre les première l'envoi normal d'interface sur votre classe à l'aide de MFC et ClassWizard, puis ajouter l'interface personnalisée plus tard. Pour l'essentiel, votre implémentation de l'interface personnalisée va simplement déléguer à l'implémentation MFC IDispatch.

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

Une instruction interface pour une interface double doit avoir la OLEAUTOMATION et attributs DUAL , et l'interface doit être dérivé de IDispatch. Vous pouvez utiliser le GUIDGEN échantillon pour créer un IID de l'interface double

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

Une fois que vous avez l'instruction interface en place, démarrez ajout d'entrées pour les propriétés et méthodes. Pour les interfaces doubles, vous devez réarranger les listes de paramètres afin que vos méthodes et fonctions d'accesseur de propriété dans l'interface double retournent un HRESULT et transmettre leurs valeurs de retour comme paramètres avec les attributs [retval,out] . N'oubliez pas que pour les propriétés, vous devez ajouter les deux une lecture ( propget ) et écrire ( propput ) fonction avec le même id d'accès. Par exemple:

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

Après que vos méthodes et les propriétés sont définies, vous devez ajouter une référence à la déclaration d'interface dans votre déclaration de la coclasse. Par exemple:

[uuid(4B115281-32F0-11cf-AC85-444553540000)]
coclasse Docume&nt
{
 nbsp ; dispinterface IAClick ;
   interface [default] IDualAClick ;
}

Une fois votre fichier ODL a été mis à jour, utilisez le mécanisme de carte interface MFC pour définir une classe d'implémentation pour l'interface double dans votre classe d'objet et de faire les entrées correspondantes dans le mécanisme de QueryInterface MFC. Vous avez besoin d'une entrée dans le INTERFACE_PART bloc pour chaque entrée dans la déclaration de l'interface de l'ODL, plus les entrées pour une interface de dispatch. Chaque entrée de FOAD avec l'attribut propput a besoin d'une fonction nommée put_propertyname . Chaque entrée avec l'attribut propget a besoin d'une fonction nomméeget_propertyname.

Pour définir une classe d'implémentation de votre interface double, ajouter un DUAL_INTERFACE_PART bloc de définition de classe de votre objet. Par exemple:

BEGIN_DUAL_INTERFACE_PART (DualAClick, IDualAClick)
  STDMETHOD(put_text) (THIS_ BSTR newText) ;
  STDMETHOD(get_text) (FAR de THIS_ BSTR * retval) ;
  STDMETHOD(put_x) (THIS_ newX court) ;
  STDMETHOD(get_X) (THIS_ court FAR * retval) ;
  STDMETHOD(put_y) (THIS_ court newY) ;
  STDMETHOD(get_Y) (THIS_ court FAR * retval) ;
  STDMETHOD(put_Position) (THIS_ IDualAutoClickPoint FAR * newPosition) ;
  STDMETHOD(get_Position) (THIS_ IDualAutoClickPoint FAR * FAR * retval) ;
  STDMETHOD(RefreshWindow)(this) ;
  STDMETHOD(SetAllProps) (THIS_ court x, y court, texte BSTR) ;
  STDMETHOD(ShowWindow)(this) ;
END_DUAL_INTERFACE_PART(DualAClick)

Pour connecter l'interface double pour MFC mécanisme de QueryInterface , ajouter un INTERFACE_PART entrée à la carte d'interface

BEGIN_INTERFACE_MAP (CAutoClickDoc, CDocument)
  INTERFACE_PART (CAutoClickDoc, DIID_IAClick, expédition)
  INTERFACE_PART (CAutoClickDoc, IID_IDualAClick, DualAClick)
END_INTERFACE_MAP()

Ensuite, vous devez remplir dans l'implémentation de l'interface. Dans la plupart des cas, vous serez capable de déléguer à l'implémentation MFC IDispatch 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);
}

Des méthodes et fonctions d'accesseur de propriété de votre objet, vous devez remplir dans la mise en œuvre. Les fonctions de votre méthode et de propriété peuvent déléguer généralement vers les méthodes générées à l'aide de ClassWizard. Toutefois, si vous définissez les propriétés pour accéder aux variables directement, vous devez écrire le code pour get/put la valeur dans la variable. Par exemple:

STDMETHODIMP CAutoClickDoc::XDualAClick::put_text(BSTR newText)
{
 nbsp ; METHOD_PROLOGUE (CAutoClickDoc, DualAClick)
   / / MFC convertit automatiquement de Unicode BSTR pour / / Ansi CString, si nécessaire...
   pThis - > m_str = NouvTexte ;
   retourne NOERROR ;
}
STDMETHODIMP CAutoClickDoc::XDualAClick::get_text(BSTR* retval)
{
   METHOD_PROLOGUE (CAutoClickDoc, DualAClick)
   / / MFC convertit automatiquement de CString Ansi pour / / Unicode BSTR, si nécessaire...
   pThis - > m_str.SetSysString(retval) ;
   retourne NOERROR ;
}

Pointeurs d'Interface double passant

Votre pointeur d'interface double n'est pas simple, surtout si vous devez appeler CCmdTarget::FromIDispatch. FromIDispatch ne fonctionne que sur des pointeurs de IDispatch de MFC. Une façon de contourner cela est d'une requête pour le jeu original du pointeur IDispatch par MFC et passer ce pointeur de fonctions qui en ont besoin. Par exemple

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

Avant de passer un pointeur à travers la méthode de double interface, vous devrez peut-être convertir le pointeur MFC IDispatch votre pointeur d'interface double. Par exemple:

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

Enregistrer la bibliothèque de types de l'Application

AppWizard ne génère pas de code pour inscrire une bibliothèque de type d'une application serveur OLE Automation dans le système. Bien qu'il y a d'autres façons de s'inscrire à la bibliothèque de types, il est commode d'avoir l'application à enregistrer la bibliothèque de types lorsqu'il met à jour ses informations de type OLE, c'est-à-dire, chaque fois que l'application est exécutée en autonome.

Pour enregistrer la bibliothèque de type de l'application chaque fois que l'application est exécutée en autonome:

Modification des paramètres de génération de projet pour tenir compte des changements de Type bibliothèque

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

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

  2. Cliquez sur l'onglet Types OLE et spécifiez un nom de fichier dans le champ nom du fichier d'en-tête de sortie. Utilisez un nom de fichier qui n'est pas déjà utilisée par votre projet, depuis MkTypLib va remplacer tout fichier existant. Cliquez sur OK pour fermer la boîte de dialogue Paramètres de construire.

Pour ajouter la définition de l'UUID du fichier d'en-tête généré MkTypLib à votre projet:

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

  2. Créez un nouveau fichier, INITIIDS.RPC et l'ajouter à votre projet. Dans ce fichier, inclure votre fichier d'en-tête généré MkTypLib après y compris OLE2.H et INITGUID.H:
    / / initIIDs.c : définit l'IID pour interfaces doubles
    / / Ce ne doit pas être construit avec un en-tête précompilé.
      # include lt;ole2.h >
      # include <initguid.h>
      # include « acdual.h »
    
  3. Dans le menu Générer, cliquez sur paramètres, puis sélectionnez INITIIDS.RPC dans la liste de fichier pour chaque configuration.

  4. Cliquez sur l'onglet C++, catégorie « en-têtes précompilés » et sélectionnez le « n'utilisant ne pas précompilé en-têtes » bouton radio. Cliquez sur OK pour fermer la boîte de dialogue Paramètres de construire.

Spécifiant le nom de la classe objet Correct dans la bibliothèque de types

Les assistants livrés avec Visual C++ mal utilisent le nom de la classe application pour spécifier la coclasse dans le fichier du serveur ODL pour classes OLE-createable. Bien que cela ne fonctionne pas, le nom de classe de mise en œuvre 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, recherchez chaque instruction de la coclasse et remplacer le nom de classe de mise en œuvre avec le nom externe correct.

Notez que lorsque la déclaration de coclass est modifiée, les noms de variables de s CLSIDdans le fichier d'en-tête généré MkTypLib changera en conséquence. Vous devez mettre à jour votre code afin d'utiliser les nouveaux noms de variables.

Manipulation des Exceptions et les Interfaces d'erreur Automation

Méthodes de votre objet automation et les fonctions d'accesseur de propriété peuvent lever des exceptions. Dans l'affirmative, vous devez les gérer dans votre implémentation de l'interface double et transmettre des informations sur l'exception vers le contrôleur via l'interface de gestion d'erreur OLE Automation, IErrorInfo. Cette interface fournit des informations d'erreur détaillées, contextuelle via les interfaces IDispatch et de VTBL. Pour indiquer qu'un gestionnaire d'erreurs est disponible, vous devez implémenter l'interface ISupportErrorInfo.

Afin d'illustrer le mécanisme de gestion des erreurs, assumer les fonctions générées par l'Assistant classe utilisées pour implémenter le support d'envoi standard de lever des exceptions. Application MFC de IDispatch::Invoke attrape généralement ces exceptions et les convertit en un EXCEPTINFO structurent qui est retourné par l'appel Invoke . Toutefois, lorsque l'interface VTBL est utilisé, vous êtes responsable pour attraper les exceptions vous-même. À titre d'exemple de la protection de vos méthodes d'interface double

STDMETHODIMP CAutoClickDoc::XDualAClick::put_text(BSTR newText)
{
 nbsp ; METHOD_PROLOGUE (CAutoClickDoc, DualAClick)
   TRY_DUAL(IID_IDualAClick)
   {
      / / MFC convertit automatiquement de Unicode BSTR pour / / Ansi CString, si nécessaire...
      pThis - > m_str = NouvTexte ;
      retourne NOERROR ;
   }
   CATCH_ALL_DUAL
}

CATCH_ALL_DUALprend soin de retourner le code erreur correct lorsqu'une exception se produit. CATCH_ALL_DUALconvertit une exception MFC en information de gestion d'erreur OLE Automation à l'aide de l'interface ICreateErrorInfo . (Par exemple CATCH_ALL_DUAL macro est dans le fichier MFCDUAL.H dans le exemple ACDUAL . La fonction, il appelle à la gestion des exceptions, DualHandleException , se trouve dans le fichier MFCDUAL.RPC.) CATCH_ALL_DUALdétermine le code d'erreur à retourner selon le type d'exception qui s'est produite

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

Tout d'abord, ajoutez du code à votre définition de classe d'automation pour montrer qu'il prend en charge ISupportErrorInfo.

Ensuite, ajoutez du code pour le mappage d'interface de la classe de votre automatisation pour associer la classe d'implémentation ISupportErrorInfo avec mécanisme de QueryInterface MFC. La INTERFACE_PART déclaration correspond à la classe définie pour ISupportErrorInfo.

Enfin, mettre en œuvre la classe définie à charge ISupportErrorInfo.

(Le exemple ACDUAL contient trois macros pour aider à faire ces trois étapes, DECLARE_DUAL_ERRORINFO , DUAL_ERRORINFO_PART , et IMPLEMENT_DUAL_ERRORINFO , tous les contenus dans MFCDUAL.H.)

L'exemple suivant implémente une classe définie à charge ISupportErrorInfo. CAutoClickDocest le nom de votre classe d'automatisation et IID_IDualAClick est l' IID de 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() {
 nbsp ; METHOD_PROLOGUE (CAutoClickDoc, SupportErrorInfo) Retour pThis - > ExternalAddRef() ; 
} CAutoClickDoc::XSupportErrorInfo::Release() STDMETHODIMP_(ULONG) {METHOD_PROLOGUE (CAutoClickDoc, SupportErrorInfo) Retour pThis - > ExternalRelease() ; 
} STDMETHODIMP CAutoClickDoc::XSupportErrorInfo::QueryInterface REFIID iid (LPVOID * ppvObj) {METHOD_PROLOGUE (CAutoClickDoc, SupportErrorInfo) Retour pThis - > ExternalQueryInterface (& iid, ppvObj) ; 
} STDMETHODIMP CAutoClickDoc::XSupportErrorInfo::InterfaceSupportsErrorInfo (REFIID iid) {METHOD_PROLOGUE (CAutoClickDoc, SupportErrorInfo) retour (iid == IID_IDualAClick) ? S_OK : S_FALSE ; 
}

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

Index