Compartir a través de


TN039: Implementación de Automation MFC/OLE

Nota:

La nota técnica siguiente no se ha actualizado desde que se incluyó por primera vez en la documentación en línea. Como resultado, algunos procedimientos y temas podrían estar obsoletos o ser incorrectos. Para obtener información más reciente, se recomienda buscar el tema de interés en el índice de la documentación en línea.

Información general de la interfaz IDispatch de OLE

La interfaz IDispatch es el medio por el que las aplicaciones exponen métodos y propiedades de modo que otras aplicaciones, como Visual BASIC u otros lenguajes, pueden usar las características de la aplicación. La parte más importante de esta interfaz es la función IDispatch::Invoke. MFC usa "mapas de envío" para implementar IDispatch::Invoke. El mapa de envío proporciona la información de implementación de MFC sobre el diseño o la "forma" de las clases derivadas de CCmdTarget, de modo que pueda manipular directamente las propiedades del objeto o llamar a funciones miembro dentro del objeto para satisfacer las solicitudes de IDispatch::Invoke.

En general, ClassWizard y MFC colaboran para ocultar la mayoría de los detalles de la automatización OLE del programador de aplicaciones. El programador se centra en la función real que se va a exponer en la aplicación y no tiene que preocuparse por los entresijos subyacentes.

Pero hay casos en los que es necesario comprender qué hace MFC en segundo plano. Esta nota abordará cómo el marco asigna DISPID a las funciones miembro y las propiedades. El conocimiento del algoritmo que usa MFC para asignar DISPIDsolo es necesario cuando necesita conocer los id., como cuando se crea una "biblioteca de tipos" para los objetos de la aplicación.

Asignación de DISPID de MFC

Aunque el usuario final de automatización (un usuario de Visual Basic, por ejemplo), ve los nombres reales de las propiedades y métodos habilitados para la automatización en su código (como obj.ShowWindow), la implementación de IDispatch::Invoke no recibe los nombres reales. Por motivos de optimización, recibe un elemento DISPID, que es una "cookie mágica" de 32 bits que describe la propiedad o el método al que se va a acceder. Estos valores DISPID se devuelven de la implementación IDispatch mediante otro método, denominado IDispatch::GetIDsOfNames. Una aplicación cliente de automatización llamará a GetIDsOfNames una vez para cada propiedad o miembro al que pretende acceder, y los almacenará en caché para llamadas posteriores a IDispatch::Invoke. De este modo, la búsqueda costosa de cadenas solo se realiza una vez por uso de objeto, en lugar de una vez por cada llamada IDispatch::Invoke.

MFC determina los DISPID para cada método y propiedad en función de dos cosas:

  • Distancia desde la parte superior del mapa de envío (en relación con 1)

  • Distancia del mapa de envío de la clase más derivada (en relación con 0)

El elemento DISPID está dividido en dos partes. La parte LOWORD de DISPID contiene el primer componente, la distancia desde la parte superior del mapa de envío. La parte HIWORD contiene la distancia desde la clase más derivada. Por ejemplo:

class CDispPoint : public CCmdTarget
{
public:
    short m_x, m_y;
    // ...
    DECLARE_DISPATCH_MAP()
    // ...
};

class CDisp3DPoint : public CDispPoint
{
public:
    short m_z;
    // ...
    DECLARE_DISPATCH_MAP()
    // ...
};

BEGIN_DISPATCH_MAP(CDispPoint, CCmdTarget)
    DISP_PROPERTY(CDispPoint, "x", m_x, VT_I2)
    DISP_PROPERTY(CDispPoint, "y", m_y, VT_I2)
END_DISPATCH_MAP()

BEGIN_DISPATCH_MAP(CDisp3DPoint, CDispPoint)
    DISP_PROPERTY(CDisp3DPoint, "z", m_z, VT_I2)
END_DISPATCH_MAP()

Como puede ver, hay dos clases, las cuales exponen interfaces de automatización OLE. Una de estas clases se deriva de la otra y, por tanto, saca provecho de la función de la clase base, incluida la parte de automatización OLE (propiedades "x" e "y" en este caso).

MFC generará DISPID para la clase CDispPoint de la siguiente manera:

property X    (DISPID)0x00000001
property Y    (DISPID)0x00000002

Puesto que las propiedades no están en una clase base, la parte HIWORD de DISPID siempre es cero (la distancia desde la clase más derivada para CDispPoint es cero).

MFC generará DISPID para la clase CDisp3DPoint de la siguiente manera:

property Z    (DISPID)0x00000001
property X    (DISPID)0x00010001
property Y    (DISPID)0x00010002

La propiedad Z recibe un elemento DISPID con una parte HIWORD que es cero, ya que se define en la clase que expone las propiedades, CDisp3DPoint. Dado que las propiedades X e Y se definen en una clase base, la parte HIWORD de DISPID es 1, ya que la clase en la que se definen estas propiedades está a una distancia de una derivación de la clase más derivada.

Nota:

La parte LOWORD siempre la determina la posición del mapa, incluso si existen entradas en el mapa con DISPID explícito (vea la sección siguiente para obtener información sobre las versiones _ID de las macros DISP_PROPERTY y DISP_FUNCTION).

Características avanzadas del mapa de envío de MFC

Hay varias características adicionales que ClassWizard no admite con esta versión de Visual C++. ClassWizard admite DISP_FUNCTION, DISP_PROPERTY y DISP_PROPERTY_EX, que definen un método, una propiedad de variable miembro y una propiedad de función miembro get/set, respectivamente. Estas capacidades suelen ser las necesarias para crear la mayoría de los servidores de automatización.

Las macros adicionales siguientes se pueden usar cuando las macros compatibles con ClassWizard no sean adecuadas: DISP_PROPERTY_NOTIFY y DISP_PROPERTY_PARAM.

Descripción de la macro DISP_PROPERTY_NOTIFY

DISP_PROPERTY_NOTIFY(
    theClass,
    pszName,
    memberName,
    pfnAfterSet,
    vtPropType)

Parámetros

theClass
Nombre de la clase.

pszName
Nombre externo de la propiedad.

memberName
Nombre de la variable miembro en la que se almacena la propiedad.

pfnAfterSet
Nombre de la función miembro a la que se va a llamar cuando se cambia la propiedad.

vtPropType
Valor que especifica el tipo de la propiedad.

Comentarios

Esta macro es muy similar a DISP_PROPERTY, salvo que acepta un argumento adicional. El argumento adicional, pfnAfterSet, debe ser una función miembro que no devuelve nada y no toma parámetros, "void OnPropertyNotify()". Se le llamará después de que se haya modificado la variable miembro.

Descripción de la macro DISP_PROPERTY_PARAM

DISP_PROPERTY_PARAM(
    theClass,
    pszName,
    pfnGet,
    pfnSet,
    vtPropType,
    vtsParams)

Parámetros

theClass
Nombre de la clase.

pszName
Nombre externo de la propiedad.

memberGet
Nombre de la función miembro usada para obtener la propiedad.

memberSet
Nombre de la función miembro usada para establecer la propiedad.

vtPropType
Valor que especifica el tipo de la propiedad.

vtsParams
Cadena de espacio separado VTS_ para cada parámetro.

Comentarios

Al igual que la macro DISP_PROPERTY_EX, esta define una propiedad a la que se accede con funciones miembro Get y Set independientes. Pero esta macro permite especificar una lista de parámetros para la propiedad. Esto resulta útil para implementar propiedades indexadas o parametrizadas de alguna otra manera. Los parámetros siempre se colocarán primero, seguidos del nuevo valor de la propiedad. Por ejemplo:

DISP_PROPERTY_PARAM(CMyObject, "item", GetItem, SetItem, VT_DISPATCH, VTS_I2 VTS_I2)

correspondería a las funciones miembro get y set:

LPDISPATCH CMyObject::GetItem(short row, short col)
void CMyObject::SetItem(short row, short col, LPDISPATCH newValue)

Descripciones de la macro DISP_XXXX_ID

DISP_FUNCTION_ID(
    theClass,
    pszName,
    dispid,
    pfnMember,
    vtRetVal,
    vtsParams)
DISP_PROPERTY_ID(
    theClass,
    pszName,
    dispid,
    memberName,
    vtPropType)
DISP_PROPERTY_NOTIFY_ID(
    theClass,
    pszName,
    dispid,
    memberName,
    pfnAfterSet,
    vtPropType)
DISP_PROPERTY_EX_ID(
    theClass,
    pszName,
    dispid,
    pfnGet,
    pfnSet,
    vtPropType)
DISP_PROPERTY_PARAM_ID(
    theClass,
    pszName,
    dispid,
    pfnGet,
    pfnSet,
    vtPropType,
    vtsParams)

Parámetros

theClass
Nombre de la clase.

pszName
Nombre externo de la propiedad.

dispid
DISPID fijado para la propiedad o el método.

pfnGet
Nombre de la función miembro usada para obtener la propiedad.

pfnSet
Nombre de la función miembro usada para establecer la propiedad.

memberName
Nombre de la variable miembro que se va a asignar a la propiedad

vtPropType
Valor que especifica el tipo de la propiedad.

vtsParams
Cadena de espacio separado VTS_ para cada parámetro.

Comentarios

Estas macros permiten especificar un elemento DISPID en lugar de permitir que MFC asigne uno automáticamente. Estas macros avanzadas tienen los mismos nombres, excepto que el id. se anexa al nombre de la macro (por ejemplo, DISP_PROPERTY_ID) y el id. lo determinado el parámetro especificado justo después del parámetro pszName. Vea AFXDISP. H para obtener más información sobre estas macros. Las entradas _ID deben colocarse al final del mapa de envío. Afectarán a la generación automática de elementos DISPID de la misma manera que una versión que no sea _ID de la macro (los elementos DISPID se determinan por posición). Por ejemplo:

BEGIN_DISPATCH_MAP(CDisp3DPoint, CCmdTarget)
    DISP_PROPERTY(CDisp3DPoint, "y", m_y, VT_I2)
    DISP_PROPERTY(CDisp3DPoint, "z", m_z, VT_I2)
    DISP_PROPERTY_ID(CDisp3DPoint, "x", 0x00020003, m_x, VT_I2)
END_DISPATCH_MAP()

MFC generará DISPID para la clase CDisp3DPoint de la siguiente manera:

property X    (DISPID)0x00020003
property Y    (DISPID)0x00000002
property Z    (DISPID)0x00000001

Especificar un elemento DISPID fijo es útil para mantener la compatibilidad con versiones anteriores a una interfaz de envío existente previamente, o para implementar determinados métodos o propiedades definidos por el sistema (normalmente vienen indicados por un elemento DISPID negativo, como la colección DISPID_NEWENUM).

Recuperación de la interfaz IDispatch para un elemento COleClientItem

Muchos servidores admitirán la automatización en sus objetos de documento, junto con la función del servidor OLE. Para obtener acceso a esta interfaz de automatización, es necesario acceder directamente a la variable miembro COleClientItem::m_lpObject. El código siguiente recuperará la interfaz IDispatch de un objeto derivado de COleClientItem. Puede incluir el código siguiente en la aplicación si encuentra esta función necesaria:

LPDISPATCH CMyClientItem::GetIDispatch()
{
    ASSERT_VALID(this);
    ASSERT(m_lpObject != NULL);

    LPUNKNOWN lpUnk = m_lpObject;

    Run();      // must be running

    LPOLELINK lpOleLink = NULL;
    if (m_lpObject->QueryInterface(IID_IOleLink,
        (LPVOID FAR*)&lpOleLink) == NOERROR)
    {
        ASSERT(lpOleLink != NULL);
        lpUnk = NULL;
        if (lpOleLink->GetBoundSource(&lpUnk) != NOERROR)
        {
            TRACE0("Warning: Link is not connected!\n");
            lpOleLink->Release();
            return NULL;
        }
        ASSERT(lpUnk != NULL);
    }

    LPDISPATCH lpDispatch = NULL;
    if (lpUnk->QueryInterface(IID_IDispatch, &lpDispatch) != NOERROR)
    {
        TRACE0("Warning: does not support IDispatch!\n");
        return NULL;
    }

    ASSERT(lpDispatch != NULL);
    return lpDispatch;
}

La interfaz de envío devuelta de esta función podría usarse directamente o adjuntarse a un elemento COleDispatchDriver para el acceso con seguridad de tipos. Si lo usa directamente, asegúrese de llamar a su miembro Release cuando pase por con el puntero (el destructor COleDispatchDriver lo hace de manera predeterminada).

Consulte también

Notas técnicas por número
Notas técnicas por categoría