Compartir a través de


TN039: Implementación de automatización de 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 incorrectos. Para obtener la información más reciente, se recomienda buscar el tema de interés en el índice de documentación en línea.

Introducción a la interfaz IDispatch OLE

La IDispatch interfaz es el medio por el que las aplicaciones exponen métodos y propiedades como 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 IDispatch::Invoke función . MFC usa "asignaciones de distribución" para implementar IDispatch::Invoke. El mapa de distribución proporciona la información de implementación de MFC sobre el diseño o la "forma" de las CCmdTargetclases derivadas, de modo que pueda manipular directamente las propiedades del objeto o llamar a funciones miembro dentro del objeto para satisfacer IDispatch::Invoke las solicitudes.

En su mayor parte, ClassWizard y MFC colaboran para ocultar la mayoría de los detalles de automatización OLE del programador de aplicaciones. El programador se centra en la funcionalidad real para exponer en la aplicación y no tiene que preocuparse por la fontanería subyacente.

Sin embargo, hay casos en los que es necesario comprender lo que MFC está haciendo en segundo plano. Esta nota abordará cómo el marco asigna DISPIDa las propiedades y funciones miembro. El conocimiento del algoritmo MFC usa para asignar DISPIDsolo es necesario cuando se necesitan conocer los identificadores, como cuando se crea una "biblioteca de tipos" para los objetos de la aplicación.

Asignación 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 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 DISPID, que es una "cookie mágica" de 32 bits que describe el método o la propiedad a la que se va a acceder. Estos valores DISPID se devuelven de la IDispatch implementación a través de otro método, denominado IDispatch::GetIDsOfNames. Una aplicación cliente de automatización llamará GetIDsOfNames una vez para cada miembro o propiedad a la que pretende acceder y almacenarlas en caché para las llamadas posteriores a IDispatch::Invoke. De este modo, la búsqueda de cadenas costosa solo se realiza una vez por uso de objeto, en lugar de una vez por IDispatch::Invoke llamada.

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

  • Distancia desde la parte superior del mapa de distribución (1 relativo)

  • Distancia del mapa de distribución de la clase más derivada (0 relativa)

El DISPID se divide en dos partes. El LOWORD del DISPID contiene el primer componente, la distancia desde la parte superior del mapa de distribución. 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, aprovecha la funcionalidad de la clase base, incluida la parte de automatización OLE (propiedades "x" e "y" en este caso).

MFC generará DISPIDs 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, el HIWORD del DISPID es siempre cero (la distancia desde la clase más derivada para CDispPoint es cero).

MFC generará DISPIDs 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 DISPID con cero HIWORD , 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, el HIWORD del 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:

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

Características avanzadas del mapa de distribución de MFC

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

Las siguientes macros adicionales se pueden usar cuando las macros compatibles con ClassWizard no son adecuadas: DISP_PROPERTY_NOTIFYy DISP_PROPERTY_PARAM.

DISP_PROPERTY_NOTIFY: descripción de macro

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.

Observaciones

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 llamará después de modificar la variable miembro.

DISP_PROPERTY_PARAM: descripción de macro

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 utilizada para establecer la propiedad .

vtPropType
Valor que especifica el tipo de la propiedad.

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

Observaciones

Al igual que la macro DISP_PROPERTY_EX, esta macro define una propiedad a la que se accede con funciones miembro Get y Set independientes. Sin embargo, esta macro permite especificar una lista de parámetros para la propiedad . Esto resulta útil para implementar propiedades que se indexan o parametrizan 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)

DISP_XXXX_ID: descripciones de macros

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.

despid
DisPID fijo 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 utilizada 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.

Observaciones

Estas macros permiten especificar un DISPID en lugar de permitir que MFC asigne automáticamente uno. Estas macros avanzadas tienen los mismos nombres, excepto que el identificador se anexa al nombre de la macro (por ejemplo, DISP_PROPERTY_ID) y el identificador viene determinado por el parámetro especificado justo después del parámetro pszName . Consulte AFXDISP. H para obtener más información sobre estas macros. Las entradas _ID deben colocarse al final del mapa de distribución. Afectarán a la generación DE DISPID automática de la misma manera que una versión no _ID de la macro (los DISPIDestán determinados 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á DISPIDs para la clase CDisp3DPoint de la siguiente manera:

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

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

Recuperación de la interfaz IDispatch para un COleClientItem

Muchos servidores admitirán la automatización dentro de sus objetos de documento, junto con la funcionalidad del servidor OLE. Para obtener acceso a esta interfaz de automatización, es necesario acceder directamente a la COleClientItem::m_lpObject variable miembro. El código siguiente recuperará la IDispatch interfaz de un objeto derivado de COleClientItem. Puede incluir el código siguiente en la aplicación si encuentra esta funcionalidad 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 distribución devuelta desde esta función podría usarse directamente o adjuntarse a para el COleDispatchDriver acceso seguro de tipos. Si lo usa directamente, asegúrese de llamar a su Release miembro cuando se realice con el puntero (el COleDispatchDriver destructor lo hace de forma predeterminada).

Consulte también

Notas técnicas por número
Notas Técnicas por Categoría