TN065: Interfaz Dual soporte para servidores de automatización OLE

Esta nota explica cómo agregar compatibilidad de interfaz dual para una aplicación de servidor basada en MFC OLE Automation. El ACDUAL muestra la compatibilidad de interfaz dual, y se toma el ejemplo de código en esta nota de ACDUAL. Las macros se describe en esta nota, tales como DECLARE_DUAL_ERRORINFO , DUAL_ERRORINFO_PART , y IMPLEMENT_DUAL_ERRORINFO , forman parte de la muestra ACDUAL y puede encontrarse en MFCDUAL.H.

Interfaces duales

Aunque la automatización OLE le permite implementar una interfaz IDispatch , una interfaz VTBL o una interfaz dual (que abarca tanto), Microsoft recomienda encarecidamente que implementar interfaces duales para todos los objetos de automatización OLE expuestos. Interfaces duales tienen ventajas significativas sobre IDispatch-únicas o sólo VTBL interfaces:

Agregar compatibilidad con interfaz Dual a una clase basada en CCmdTarget.

Una interfaz dual es realmente sólo una interfaz personalizada derivada de IDispatch. La forma más sencilla de implementar la compatibilidad de interfaz dual en un CCmdTarget.-clase base es primer implementar la interfaz en la clase utilizando MFC y ClassWizard el envío normal y, a continuación, agregue la interfaz personalizada más tarde. En su mayor parte, la implementación de la interfaz personalizada simplemente delegará a la aplicación MFC IDispatch.

En primer lugar, modificar el archivo ODL de su servidor para definir interfaces duales para sus objetos. Para definir una interfaz dual, debe utilizar una declaración de interfaz, en lugar de la DISPINTERFACE declaración que generan los asistentes de Visual C++. En lugar de eliminar los existentes DISPINTERFACE declaración, agregar una nueva declaración de interfaz. Manteniendo el DISPINTERFACE formulario, usted puede continuar utilizando ClassWizard para añadir propiedades y métodos a su objeto, pero debe agregar los métodos y propiedades equivalentes a su declaración de interfaz.

Una declaración de interfaz para una interfaz dual debe tener los atributos DUAL y OLEAUTOMATION , y la interfaz debe derivar de IDispatch. Puede utilizar el GUIDGEN muestra para crear un IID de la interfaz dual

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

Una vez que la declaración de interfaz en su lugar, empezar a agregar entradas para los métodos y propiedades. Para interfaces duales, es necesario reorganizar las listas de parámetros para que sus métodos y funciones de descriptor de acceso de propiedad en la interfaz dual devolución un valor HRESULT y pasan sus valores devueltos como parámetros con los atributos [retval,out] . Recuerde que las propiedades, deberá agregar una lectura ( propget ) y escribir ( propput ) acceder a la función con el mismo id. Por ejemplo:

[propput, grupos] Texto HRESULT ([en] BSTR newText);
[propget, grupos] Texto HRESULT ([out, retval] BSTR * retval)

Después de que sus métodos y propiedades están definidas, debe agregar una referencia a la declaración de interfaz en su declaración de coclase. Por ejemplo:

[uuid(4B115281-32F0-11cf-AC85-444553540000)]
coclase docume&nto
{
 nbsp; interfaz dispinterface IAClick;
   interfaz [predeterminada] IDualAClick;
}

Una vez que se actualizó el archivo ODL, utilizar el mecanismo de mapa de interfaz de MFC para definir una clase de implementación de la interfaz dual en la clase de objeto y que las entradas correspondientes en mecanismo de QueryInterface de MFC. Necesita una entrada en el INTERFACE_PART bloque para cada entrada en la declaración de interfaz de la ODL, además de las entradas para una interfaz de envío. Cada entrada ODL con el atributo propput necesita una función denominada put_propertyname . Cada entrada con el atributo propget necesita una función denominadaget_propertyname.

Para definir una clase de implementación de la interfaz dual, agregue un DUAL_INTERFACE_PART bloque a la definición de clase de objeto. Por ejemplo:

BEGIN_DUAL_INTERFACE_PART (DualAClick, IDualAClick)
  STDMETHOD(put_text) (BSTR THIS_ newText);
  STDMETHOD(get_text) (THIS_ lejano de BSTR * retval);
  STDMETHOD(put_x) (THIS_ corto newX);
  STDMETHOD(get_x) (THIS_ corto lejano * retval);
  STDMETHOD(put_y) (THIS_ corto newY);
  STDMETHOD(get_y) (THIS_ corto lejano * retval);
  STDMETHOD(put_Position) (THIS_ IDualAutoClickPoint FAR * newPosition);
  STDMETHOD(get_Position) (THIS_ IDualAutoClickPoint FAR * extremo * retval);
  STDMETHOD(RefreshWindow)(this);
  STDMETHOD(SetAllProps) (THIS_ x corto, y corto, texto BSTR);
  STDMETHOD(ShowWindow)(this);
END_DUAL_INTERFACE_PART(DualAClick)

Para conectar la interfaz dual de MFC mecanismo de QueryInterface , agregar una INTERFACE_PART entrada al mapa de interfaz

BEGIN_INTERFACE_MAP (CAutoClickDoc, CDocument)
  INTERFACE_PART (CAutoClickDoc, DIID_IAClick, envío)
  INTERFACE_PART (CAutoClickDoc, IID_IDualAClick, DualAClick)
END_INTERFACE_MAP()

A continuación, debe llenar en la implementación de la interfaz. En su mayor parte, podrá delegar en la implementación de MFC IDispatch existente. Por ejemplo:

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);
}

Para los métodos y funciones de descriptor de acceso de propiedad del objeto, necesita llenar la aplicación. Generalmente pueden delegar las funciones de su método y la propiedad hacia los métodos generados mediante ClassWizard. Sin embargo, si establece las propiedades para acceder a las variables directamente, necesita escribir el código para obtener y colocar el valor en la variable. Por ejemplo:

STDMETHODIMP CAutoClickDoc::XDualAClick::put_text(BSTR newText)
{
 nbsp; METHOD_PROLOGUE (CAutoClickDoc, DualAClick)
   / / MFC convierte automáticamente de BSTR Unicode para / / Ansi CString, si es necesario...
   pThis - > m_str = newText;
   Return NOERROR;
}
STDMETHODIMP CAutoClickDoc::XDualAClick::get_text(BSTR* retval)
{
   METHOD_PROLOGUE (CAutoClickDoc, DualAClick)
   / / MFC convierte automáticamente de CString Ansi para / / BSTR Unicode, si es necesario...
   pThis - > m_str.SetSysString(retval);
   Return NOERROR;
}

Pasar punteros de interfaz Dual

Pase el puntero de interfaz dual no es sencillo, especialmente si necesita llamar a CCmdTarget::FromIDispatch. FromIDispatch sólo funciona en los punteros de IDispatch de MFC. Es una forma de solucionar este problema de consulta para el conjunto original de puntero IDispatch de MFC y pasar ese puntero a las funciones que necesiten. Por ejemplo

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

Antes de pasar un puntero a través del método de interfaz dual, puede que deba convertir desde el puntero MFC IDispatch para el puntero de interfaz dual. Por ejemplo:

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

Registrar la biblioteca de tipos de la aplicación

AppWizard no genera código para registrar la biblioteca de tipos de una aplicación de servidor de automatización OLE con el sistema. Mientras que hay otras maneras de registrar la biblioteca de tipos, es conveniente disponer la aplicación registrar la biblioteca de tipos cuando está actualizando la información de tipo OLE, es decir, cada vez que se ejecute la aplicación independiente.

Para registrar la biblioteca de tipos de la aplicación cuando se ejecute la aplicación independiente:

Modificar la configuración de generación del proyecto para acomodar los cambios de tipo biblioteca

Para modificar un proyecto construir ajustes por lo que se genera un archivo de encabezado que contiene definiciones de UUID por MkTypLib siempre es reconstruir la biblioteca de tipos

  1. En el menú Generar, haga clic en configuración y, a continuación, seleccione el archivo ODL desde la lista de archivos para cada configuración.

  2. Haga clic en la ficha tipos de OLE y especifique un nombre de archivo en el campo de nombre de archivo de encabezado de salida. Utilice un nombre de archivo que ya no se utiliza por su proyecto, ya que MkTypLib sobrescribirá cualquier archivo existente. Haga clic en Aceptar para cerrar el cuadro de diálogo de configuración de construir.

Para agregar las definiciones de UUID desde el archivo de encabezado generado MkTypLib al proyecto:

  1. Incluir la MkTypLib genera archivo de encabezado en su estándar incluye el archivo de encabezado, STDAFX.H.

  2. Crear un archivo nuevo, INITIIDS.CPP y agregarlo al proyecto. En este archivo, incluir el archivo de encabezado generado MkTypLib después incluyendo OLE2.H y INITGUID.H:
    / / initIIDs.c: define IID para interfaces duales
    / / Esto no debe ser construida con encabezado precompilado.
      # include lt;ole2.h >
      # include <initguid.h>
      # include "acdual.h"
    
  3. En el menú Generar, haga clic en configuración y, a continuación, seleccione INITIIDS.CPP de la lista de archivos para cada configuración.

  4. Haga clic en la ficha de C++, categoría "encabezados precompilados" y seleccione el "no uso precompilados encabezados" botón de radio. Haga clic en Aceptar para cerrar el cuadro de diálogo de configuración de construir.

Especifica el nombre de clase de objeto correcto en la biblioteca de tipos

Los asistentes enviados incorrectamente con Visual C++ utilizan el nombre de la clase de aplicación para especificar la coclase en el archivo del servidor ODL para clases OLE createable. Mientras que esto funcionará, el nombre de la clase de aplicación probablemente no es el nombre de clase que desea que los usuarios de su objeto a utilizar. Para especificar el nombre correcto, abra el archivo ODL, busque cada instrucción coclase y reemplazar el nombre de la clase de aplicación con el nombre externo correcto.

Tenga en cuenta que cuando se cambia la instrucción coclase, los nombres de variable de s CLSIDen el archivo de encabezado generado MkTypLib cambiará en consecuencia. Necesita actualizar el código para utilizar los nuevos nombres de variables.

Manejo de excepciones y las Interfaces de automatización de Error

Funciones descriptoras de acceso de propiedades y métodos del objeto de automatización pueden producir excepciones. Si es así, debe manejar en su implementación de interfaz dual y pasar información acerca de la excepción a la controladora a través de la interfaz de control de errores de automatización OLE, IErrorInfo. Esta interfaz proporciona información de error detallada y contextual a través de interfaces IDispatch y VTBL. Para indicar que un controlador de errores está disponible, debe implementar la interfaz ISupportErrorInfo.

Para ilustrar el mecanismo de control de errores, asumir que las funciones generadas por el ClassWizard utilizadas para implementar el soporte de envío estándar inician excepciones. Aplicación de MFC de IDispatch:: Invoke normalmente capta estas excepciones y se convierte en un EXCEPTINFO estructuran que es devuelto a través de la llamada Invoke . Sin embargo, cuando se utiliza la interfaz VTBL, usted es responsable de detectar las excepciones usted mismo. Como un ejemplo de la protección de sus métodos de interfaz dual

STDMETHODIMP CAutoClickDoc::XDualAClick::put_text(BSTR newText)
{
 nbsp; METHOD_PROLOGUE (CAutoClickDoc, DualAClick)
   TRY_DUAL(IID_IDualAClick)
   {
      / / MFC convierte automáticamente de BSTR Unicode para / / Ansi CString, si es necesario...
      pThis - > m_str = newText;
      Return NOERROR;
   }
   CATCH_ALL_DUAL
}

CATCH_ALL_DUALse encarga de devolver el código de error correcto cuando se produce una excepción. CATCH_ALL_DUALconvierte una excepción de MFC en información de control de errores de automatización OLE mediante la interfaz ICreateErrorInfo . (Un ejemplo CATCH_ALL_DUAL macro está en el archivo MFCDUAL.H en el ACDUAL muestra. La función llama a controlar excepciones, DualHandleException , está en el archivo MFCDUAL.CPP.) CATCH_ALL_DUALdetermina el código de error para volver en función del tipo de excepción que se produjo

Para indicar que se utiliza el controlador de errores de automatización OLE, también debe implementar la interfaz ISupportErrorInfo.

En primer lugar, agregue código para la definición de clase de automatización para mostrar es compatible con ISupportErrorInfo.

En segundo lugar, agregue código al mapa de interfaz de la clase de automatización para asociar la clase de aplicación ISupportErrorInfo con mecanismo de QueryInterface de MFC. La INTERFACE_PART declaración coincide con la clase definida para ISupportErrorInfo.

Por último, implementar la clase definida para apoyar ISupportErrorInfo.

(El ACDUAL muestra contiene tres macros para ayudar a hacer estos tres pasos, DECLARE_DUAL_ERRORINFO , DUAL_ERRORINFO_PART , y IMPLEMENT_DUAL_ERRORINFO , todos figuran en MFCDUAL.H.)

En el ejemplo siguiente se implementa una clase definida para apoyar ISupportErrorInfo. CAutoClickDoces el nombre de la clase de automatización y IID_IDualAClick es el IID de la interfaz que es la fuente de errores informó mediante el objeto de error de automatización OLE:

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

&Notas técnicas por número |nbsp; Notas técnicas por categoría

Index