Compartir a través de


TN038: Implementación de IUnknown en 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.

En el corazón de OLE 2 se encuentra el "Modelo de objetos de componente OLE" o COM. COM define un estándar para la forma en que los objetos de cooperación se comunican entre sí. Esto incluye los detalles del aspecto de un "objeto", incluido cómo se envían los métodos en un objeto. COM también define una clase base a partir de la cual se derivan todas las clases compatibles con COM. Esta clase base es IUnknown. Aunque la interfaz IUnknown se conoce como una clase de C++, COM no es específica de ningún lenguaje, se puede implementar en C, PASCAL o cualquier otro lenguaje que pueda admitir el diseño binario de un objeto COM.

OLE hace referencia a todas las clases derivadas de IUnknown como "interfaces". Se trata de una distinción importante, ya que una "interfaz" como IUnknown lleva sin implementación. Simplemente define el protocolo por el que se comunican los objetos, no los detalles de lo que hacen esas implementaciones. Esto es razonable para un sistema que permite la máxima flexibilidad. Es el trabajo de MFC implementar un comportamiento predeterminado para los programas MFC/C++.

Para comprender la implementación de MFC de IUnknown primero debe comprender lo que es esta interfaz. A continuación se define una versión simplificada de IUnknown:

class IUnknown
{
public:
    virtual HRESULT QueryInterface(REFIID iid, void** ppvObj) = 0;
    virtual ULONG AddRef() = 0;
    virtual ULONG Release() = 0;
};

Nota

Algunos detalles necesarios de la convención de llamada, como __stdcall, se dejan fuera para esta ilustración.

Las funciones miembro AddRef y Release controlan la administración de memoria del objeto. COM usa un esquema de recuento de referencias para realizar un seguimiento de los objetos. Nunca se hace referencia a un objeto directamente como lo haría en C++. En su lugar, siempre se hace referencia a los objetos COM a través de un puntero. Para liberar el objeto cuando el propietario haya terminado de usarlo, se llama al miembro Release del objeto (en lugar de usar el operador delete, como se haría para un objeto de C++ tradicional). El mecanismo de recuento de referencias permite administrar varias referencias a un único objeto. Una implementación de AddRef y Release mantiene un recuento de referencias en el objeto ; el objeto no se elimina hasta que su recuento de referencias alcanza cero.

addRef y release son bastante sencillos desde el punto de vista de la implementación. Esta es una implementación trivial:

ULONG CMyObj::AddRef()
{
    return ++m_dwRef;
}

ULONG CMyObj::Release()
{
    if (--m_dwRef == 0)
    {
        delete this;
        return 0;
    }
    return m_dwRef;
}

La función miembro QueryInterface es un poco más interesante. No es muy interesante tener un objeto cuyas únicas funciones miembro son AddRef y Release — sería bueno poder indicarle al objeto que haga más cosas de las que IUnknown proporciona. Aquí es donde QueryInterface es útil. Permite obtener una "interfaz" diferente en el mismo objeto. Estas interfaces se derivan normalmente de IUnknown y agregan funcionalidad adicional mediante la adición de nuevas funciones miembro. Las interfaces COM nunca tienen variables miembro declaradas en la interfaz y todas las funciones miembro se declaran como pura virtual. Por ejemplo

class IPrintInterface : public IUnknown
{
public:
    virtual void PrintObject() = 0;
};

Para obtener una IPrintInterface si solo tiene un IUnknown, llame a QueryInterface mediante el IID de la IPrintInterface. Un IID es un número de 128 bits que identifica de forma única la interfaz. Hay un IID para cada interfaz que usted o OLE definen. Si pUnk es un puntero a un objeto IUnknown, el código para recuperar una IPrintInterface podría ser:

IPrintInterface* pPrint = NULL;
if (pUnk->QueryInterface(IID_IPrintInterface, (void**)&pPrint) == NOERROR)
{
    pPrint->PrintObject();
    pPrint->Release();
    // release pointer obtained via QueryInterface
}

Esto parece bastante fácil, pero cómo implementaría un objeto que admita tanto IPrintInterface como interfaz IUnknown En este caso es sencillo, ya que IPrintInterface se deriva directamente de IUnknown, mediante la implementación de IPrintInterface, IUnknown se admite automáticamente. Por ejemplo:

class CPrintObj : public CPrintInterface
{
    virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
    virtual ULONG AddRef();
    virtual ULONG Release();
    virtual void PrintObject();
};

Las implementaciones de AddRef y Release serían exactamente iguales que las implementadas anteriormente. CPrintObj::QueryInterface tendría un aspecto similar al siguiente:

HRESULT CPrintObj::QueryInterface(REFIID iid, void FAR* FAR* ppvObj)
{
    if (iid == IID_IUnknown || iid == IID_IPrintInterface)
    {
        *ppvObj = this;
        AddRef();
        return NOERROR;
    }
    return E_NOINTERFACE;
}

Como puede ver, si se reconoce el identificador de interfaz (IID), se devuelve un puntero al objeto; de lo contrario, se produce un error. Tenga en cuenta también que una correcta QueryInterface da como resultado una AddRef implícita. Por supuesto, también tendrás que implementar CEditObj::Print. Esto es sencillo porque IPrintInterface se deriva directamente de la interfaz IUnknown. Sin embargo, si desea admitir dos interfaces diferentes, ambas derivadas de IUnknown, tenga en cuenta lo siguiente:

class IEditInterface : public IUnkown
{
public:
    virtual void EditObject() = 0;
};

Aunque hay varias maneras diferentes de implementar una clase que admita IEditInterface e IPrintInterface, incluido el uso de la herencia múltiple de C++, esta nota se concentrará en el uso de clases anidadas para implementar esta funcionalidad.

class CEditPrintObj
{
public:
    CEditPrintObj();

    HRESULT QueryInterface(REFIID iid, void**);
    ULONG AddRef();
    ULONG Release();
    DWORD m_dwRef;

    class CPrintObj : public IPrintInterface
    {
    public:
        CEditPrintObj* m_pParent;
        virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
        virtual ULONG AddRef();
        virtual ULONG Release();
    } m_printObj;

    class CEditObj : public IEditInterface
    {
    public:
        CEditPrintObj* m_pParent;
        virtual ULONG QueryInterface(REFIID iid, void** ppvObj);
        virtual ULONG AddRef();
        virtual ULONG Release();
    } m_editObj;
};

A continuación se incluye toda la implementación:

CEditPrintObj::CEditPrintObj()
{
    m_editObj.m_pParent = this;
    m_printObj.m_pParent = this;
}

ULONG CEditPrintObj::AddRef()
{
    return ++m_dwRef;
}

CEditPrintObj::Release()
{
    if (--m_dwRef == 0)
    {
        delete this;
        return 0;
    }
    return m_dwRef;
}

HRESULT CEditPrintObj::QueryInterface(REFIID iid, void** ppvObj)
{
    if (iid == IID_IUnknown || iid == IID_IPrintInterface)
    {
        *ppvObj = &m_printObj;
        AddRef();
        return NOERROR;
    }
    else if (iid == IID_IEditInterface)
    {
        *ppvObj = &m_editObj;
        AddRef();
        return NOERROR;
    }
    return E_NOINTERFACE;
}

ULONG CEditPrintObj::CEditObj::AddRef()
{
    return m_pParent->AddRef();
}

ULONG CEditPrintObj::CEditObj::Release()
{
    return m_pParent->Release();
}

HRESULT CEditPrintObj::CEditObj::QueryInterface(REFIID iid, void** ppvObj)
{
    return m_pParent->QueryInterface(iid, ppvObj);
}

ULONG CEditPrintObj::CPrintObj::AddRef()
{
    return m_pParent->AddRef();
}

ULONG CEditPrintObj::CPrintObj::Release()
{
    return m_pParent->Release();
}

HRESULT CEditPrintObj::CPrintObj::QueryInterface(REFIID iid, void** ppvObj)
{
    return m_pParent->QueryInterface(iid, ppvObj);
}

Observe que la mayor parte de la implementación de IUnknown se coloca en la clase CEditPrintObj en lugar de duplicar el código en CEditPrintObj::CEditObj y CEditPrintObj::CPrintObj. Esto reduce la cantidad de código y evita errores. El punto clave aquí es que desde la interfaz IUnknown es posible llamar a QueryInterface recuperar cualquier interfaz que pueda admitir el objeto y de cada una de esas interfaces es posible hacer lo mismo. Esto significa que todas las funciones QueryInterface disponibles en cada interfaz deben comportarse exactamente de la misma manera. Para que estos objetos incrustados llamen a la implementación en el “objeto externo”, se usa un puntero (m_pParent). El puntero m_pParent se inicializa durante el constructor CEditPrintObj. A continuación, debería implementar también CEditPrintObj::CPrintObj::PrintObject y CEditPrintObj::CEditObj::EditObject. Se ha agregado un poco de código para agregar una característica, la capacidad de editar el objeto. Afortunadamente, es bastante raro que las interfaces solo tengan una sola función miembro (aunque sucede) y, en este caso, EditObject y PrintObject normalmente se combinarían en una sola interfaz.

Esa es una gran explicación y un montón de código para un escenario tan sencillo. Las clases MFC/OLE proporcionan una alternativa más sencilla. La implementación de MFC usa una técnica similar a la forma en que los mensajes de Windows se encapsulan con mapas de mensajes. Esta instalación se denomina mapas de interfaz y se describe en la sección siguiente.

Mapas de interfaz MFC

MFC/OLE incluye una implementación de "Mapas de interfaz" similar a "Mapas de mensajes" y "Mapas de distribución" de MFC en concepto y ejecución. Las características principales de los mapas de interfaz de MFC son las siguientes:

  • Implementación estándar de IUnknown, integrada en la clase CCmdTarget.

  • Mantenimiento de recuento de referencias, modificado por AddRef y Release

  • Implementación controlada por datos de QueryInterface

Además, los mapas de interfaz admiten las siguientes características avanzadas:

  • Compatibilidad con la creación de objetos COM aggregatables

  • Compatibilidad con el uso de objetos agregados en la implementación de un objeto COM

  • La implementación es enlazable y extensible

Para obtener más información sobre la agregación, consulte el tema Agregación.

El soporte del mapa de interfaz de MFC se basa en la clase CCmdTarget. CCmdTargettiene un” recuento de referencias, así como todas las funciones miembro asociadas a la implementación de IUnknown (por ejemplo, el recuento de referencias se encuentra en CCmdTarget). Para crear una clase que admita OLE COM, derive una clase de CCmdTarget y use varias macros, así como funciones miembro de CCmdTarget para implementar las interfaces deseadas. La implementación de MFC usa clases anidadas para definir cada implementación de interfaz de forma muy similar al ejemplo anterior. Esto se hace más fácil con una implementación estándar de IUnknown, así como una serie de macros que eliminan parte del código repetitivo.

Conceptos básicos de mapa de interfaz

Para implementar una clase mediante mapas de interfaz de MFC

  1. Derive una clase directa o indirectamente de CCmdTarget.

  2. Use la función DECLARE_INTERFACE_MAP en la definición de clase derivada.

  3. Para cada interfaz que quiera admitir, use las macros BEGIN_INTERFACE_PART y END_INTERFACE_PART en la definición de clase.

  4. En el archivo de implementación, use las macros BEGIN_INTERFACE_MAP y END_INTERFACE_MAP para definir el mapa de interfaz de la clase.

  5. Para cada IID compatible, use la macro INTERFACE_PART entre las macros BEGIN_INTERFACE_MAP y END_INTERFACE_MAP para asignar ese IID a una "parte" específica de la clase.

  6. Implemente cada una de las clases anidadas que representan las interfaces con las que se ofrece compatibilidad.

  7. Use la macro METHOD_PROLOGUE para acceder al objeto principal derivado de CCmdTarget.

  8. AddRef, Release y QueryInterface pueden delegarse en la implementación de CCmdTarget de estas funciones (ExternalAddRef, ExternalRelease y ExternalQueryInterface).

El ejemplo de CPrintEditObj anterior podría implementarse de la siguiente manera:

class CPrintEditObj : public CCmdTarget
{
public:
    // member data and member functions for CPrintEditObj go here

// Interface Maps
protected:
    DECLARE_INTERFACE_MAP()

    BEGIN_INTERFACE_PART(EditObj, IEditInterface)
        STDMETHOD_(void, EditObject)();
    END_INTERFACE_PART(EditObj)

    BEGIN_INTERFACE_PART(PrintObj, IPrintInterface)
        STDMETHOD_(void, PrintObject)();
    END_INTERFACE_PART(PrintObj)
};

La declaración anterior crea una clase derivada de CCmdTarget. La macro DECLARE_INTERFACE_MAP indica al marco de trabajo que esta clase tendrá un mapa de interfaz personalizado. Además, las macros BEGIN_INTERFACE_PART y END_INTERFACE_PART definen clases anidadas, en este caso con nombres CEditObj y CPrintObj (la X solo se usa para diferenciar las clases anidadas de las clases globales que comienzan por "C" y clases de interfaz que comienzan por "I"). Se crean dos miembros anidados de estas clases: m_CEditObj y m_CPrintObj, respectivamente. Las macros declaran automáticamente las funciones de AddRef, Releasey QueryInterface; Por lo tanto, solo se declaran las funciones específicas de esta interfaz: EditObject y PrintObject (se usa la macro OLE STDMETHOD para que se proporcionen _stdcall y palabras clave virtuales según corresponda para la plataforma de destino).

Para implementar el mapa de interfaz para esta clase:

BEGIN_INTERFACE_MAP(CPrintEditObj, CCmdTarget)
    INTERFACE_PART(CPrintEditObj, IID_IPrintInterface, PrintObj)
    INTERFACE_PART(CPrintEditObj, IID_IEditInterface, EditObj)
END_INTERFACE_MAP()

Esto conecta el IID de IID_IPrintInterface con m_CPrintObj y IID_IEditInterface con m_CEditObj respectivamente. La implementación de CCmdTarget de QueryInterface (CCmdTarget::ExternalQueryInterface) usa este mapa para devolver punteros a m_CPrintObj y a m_CEditObj cuando se solicite. No es necesario incluir una entrada para IID_IUnknown; El marco usará la primera interfaz del mapa (en este caso, m_CPrintObj) cuando se solicite IID_IUnknown.

Aunque la macro BEGIN_INTERFACE_PART declara automáticamente las funciones AddRef, Release y QueryInterface, aún deberá implementarlas.

ULONG FAR EXPORT CEditPrintObj::XEditObj::AddRef()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return pThis->ExternalAddRef();
}

ULONG FAR EXPORT CEditPrintObj::XEditObj::Release()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return pThis->ExternalRelease();
}

HRESULT FAR EXPORT CEditPrintObj::XEditObj::QueryInterface(
    REFIID iid,
    void FAR* FAR* ppvObj)
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}

void FAR EXPORT CEditPrintObj::XEditObj::EditObject()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    // code to "Edit" the object, whatever that means...
}

La implementación de CEditPrintObj::CPrintObj sería similar a las definiciones anteriores para CEditPrintObj::CEditObj. Aunque sería posible crear una macro que se pudiera usar para generar automáticamente estas funciones (pero anteriormente en el desarrollo de MFC/OLE, este era el caso), resulta difícil establecer puntos de interrupción cuando una macro genera más de una línea de código. Por este motivo, este código se expande manualmente.

Mediante el uso de la implementación del marco de trabajo de mapas de mensajes, no se tuvieron que realizar varias cosas:

  • Implementar QueryInterface

  • Implementar AddRef y Release

  • Declarar cualquiera de estos métodos integrados en ambas interfaces

Además, el framework utiliza mapas de mensajes internamente. Esto le permite derivar de una clase de marco, por ejemplo, COleServerDoc, que ya admite determinadas interfaces y proporciona reemplazos o adiciones a las interfaces proporcionadas por el marco. Puede hacerlo porque el marco es totalmente compatible con la herencia de un mapa de interfaz de una clase base. Esa es la razón por la que BEGIN_INTERFACE_MAP toma como segundo parámetro el nombre de la clase base.

Nota

Por lo general, no es posible reutilizar las implementaciones integradas de las interfaces OLE de MFC simplemente heredando la especialización incorporada de esa interfaz de la versión MFC. Esto no es posible porque el uso de la macro METHOD_PROLOGUE para tener acceso al objeto contenedor derivado de CCmdTarget implica un desplazamiento fijo del objeto incrustado desde el objeto derivado de CCmdTarget. Esto significa, por ejemplo, que no se puede derivar un XMyAdviseSink embebido de la implementación de MFC en COleClientItem::XAdviseSink, ya que XAdviseSink se basa en estar a un desplazamiento específico desde la parte superior del objeto COleClientItem.

Nota

Sin embargo, puede delegar en la implementación de MFC para todas las funciones en las que desea que se mantenga el comportamiento predeterminado de MFC. Esto se hace en la implementación de MFC de IOleInPlaceFrame (XOleInPlaceFrame) en la clase COleFrameHook (se delega en m_xOleInPlaceUIWindow para muchas funciones). Este diseño se eligió para reducir el tamaño en tiempo de ejecución de los objetos que implementan muchas interfaces; elimina la necesidad de un puntero atrás (por ejemplo, la forma en que se usó m_pParent en la sección anterior).

Mapas de agregación e interfaz

Además de admitir objetos COM independientes, MFC también admite la agregación. La propia agregación es un tema demasiado complejo para discutir aquí; consulta el tema agregación para obtener más información sobre la agregación. Esta nota simplemente describirá la compatibilidad con la agregación integrada en el marco y los mapas de interfaz.

Hay dos maneras de usar la agregación: (1) mediante un objeto COM que admite la agregación y (2) que implementa un objeto que otro puede agregar. Estas capacidades se pueden denominar "usar un objeto agregado" y "hacer que un objeto sea agregable". MFC es compatible con ambas capacidades.

Uso de un objeto agregado

Para usar un objeto agregado, debe haber alguna manera de vincular el agregado al mecanismo QueryInterface. Es decir, el objeto agregado debe comportarse como si fuera una parte nativa del objeto. Por lo tanto, ¿cómo se asocia esto al mecanismo de asignación de interfaz de MFC? Además de la macro INTERFACE_PART, donde un objeto anidado se asigna a un IID, también puede declarar un objeto agregado como parte de su clase derivada CCmdTarget. Para ello, se usa la macro INTERFACE_AGGREGATE. Esto le permite especificar una variable miembro (que debe ser un puntero a un IUnknown o a una clase derivada), que se integrará en el mecanismo de mapeo de interfaces. Si el puntero no es NULL cuando se llama a CCmdTarget::ExternalQueryInterface, el marco de trabajo llamará automáticamente a la función miembro QueryInterface del objeto agregado, si el IID solicitado no es uno de los IID nativos compatibles con el propio objeto CCmdTarget.

Para usar la macro INTERFACE_AGGREGATE

  1. Declare una variable miembro (un IUnknown*) que contendrá un puntero al objeto agregado.

  2. Incluya una macro INTERFACE_AGGREGATE en el mapa de interfaz, que hace referencia a la variable miembro por nombre.

  3. En algún momento (normalmente durante CCmdTarget::OnCreateAggregates), inicialice la variable miembro en algo distinto de NULL.

Por ejemplo:

class CAggrExample : public CCmdTarget
{
public:
    CAggrExample();

protected:
    LPUNKNOWN m_lpAggrInner;
    virtual BOOL OnCreateAggregates();

    DECLARE_INTERFACE_MAP()
    // "native" interface part macros may be used here
};

CAggrExample::CAggrExample()
{
    m_lpAggrInner = NULL;
}

BOOL CAggrExample::OnCreateAggregates()
{
    // wire up aggregate with correct controlling unknown
    m_lpAggrInner = CoCreateInstance(CLSID_Example,
        GetControllingUnknown(), CLSCTX_INPROC_SERVER,
        IID_IUnknown, (LPVOID*)&m_lpAggrInner);

    if (m_lpAggrInner == NULL)
        return FALSE;
    // optionally, create other aggregate objects here
    return TRUE;
}

BEGIN_INTERFACE_MAP(CAggrExample, CCmdTarget)
    // native "INTERFACE_PART" entries go here
    INTERFACE_AGGREGATE(CAggrExample, m_lpAggrInner)
END_INTERFACE_MAP()

La variable m_lpAggrInner se inicializa en el constructor en NULL. El marco omite una variable miembro NULL en la implementación predeterminada de QueryInterface. OnCreateAggregates es un buen lugar para realmente crear los objetos agregados. Tendrá que llamarlo explícitamente si va a crear el objeto fuera de la implementación de MFC de COleObjectFactory. El motivo para crear agregados en CCmdTarget::OnCreateAggregates, así como el uso de CCmdTarget::GetControllingUnknown, se hará evidente cuando se discuta la creación de objetos agregables.

Esta técnica proporcionará al objeto todas las interfaces que admite el objeto agregado además de sus interfaces nativas. Si solo desea un subconjunto de las interfaces que admite el agregado, puede invalidar CCmdTarget::GetInterfaceHook. Esto le permite una capacidad de enlace de muy bajo nivel, similar a QueryInterface. Lo normal es querer todas las interfaces que sean compatibles con el agregado.

Crear una implementación de objeto agregable

Para que un objeto sea aggregatable, la implementación de AddRef, Release y QueryInterface debe delegar en un "control desconocido". Es decir, para que forme parte del objeto, debe delegar AddRef, Release y QueryInterface en un objeto diferente, también derivado de IUnknown. Este “desconocido de control” se proporciona al objeto cuando se crea, es decir, se proporciona a la implementación de COleObjectFactory. La implementación de esto conlleva una pequeña cantidad de sobrecarga y, en algunos casos, no es deseable, por lo que MFC hace que sea opcional. Para permitir que un objeto sea aggregatable, llame a CCmdTarget::EnableAggregation desde el constructor del objeto.

Si el objeto también utiliza agregados, asegúrese de pasar el “desconocido de control” correcto a los objetos agregados. Normalmente este puntero IUnknown se pasa al objeto cuando se crea el agregado. Por ejemplo, el parámetro pUnkOuter es el “desconocido de control” para los objetos creados con CoCreateInstance. El puntero correcto “desconocido de control” se puede recuperar llamando a CCmdTarget::GetControllingUnknown. Sin embargo, el valor devuelto de esa función no es válido durante el constructor. Por este motivo, se recomienda crear los agregados solo al reemplazar CCmdTarget::OnCreateAggregates, donde se puede confiar en el valor devuelto desde GetControllingUnknown, aunque se haya creado a partir de la implementación de COleObjectFactory.

También es importante que el objeto manipule el recuento de referencia correcto al agregar o liberar recuentos de referencia artificiales. Para asegurarse de que este es el caso, llame siempre a ExternalAddRef y ExternalRelease en lugar de InternalRelease y InternalAddRef. Es raro llamar a InternalRelease o InternalAddRef en una clase que admita la agregación.

Material de referencia

El uso avanzado de OLE, como definir sus propias interfaces o reemplazar la implementación del marco de las interfaces OLE requiere el uso del mecanismo de mapa de interfaz subyacente.

En esta sección se describe cada macro y las API que se usan para implementar estas características avanzadas.

CCmdTarget::EnableAggregation : Descripción de la función

void EnableAggregation();

Observaciones

Llame a esta función en el constructor de la clase derivada si desea admitir la agregación OLE para objetos de este tipo. Esto prepara una implementación especial de IUnknown que es necesaria para objetos aggregatables.

CCmdTarget::ExternalQueryInterface: Descripción de la función

DWORD ExternalQueryInterface(
    const void FAR* lpIID,
    LPVOIDFAR* ppvObj
);

Parámetros

lpIID
Un puntero lejano a un IID (el primer argumento para QueryInterface)

ppvObj
Un puntero a un IUnknown* (el segundo argumento para QueryInterface)

Observaciones

Llame a esta función en la implementación de IUnknown para cada interfaz que implemente la clase. Esta función proporciona la implementación estándar controlada por datos de QueryInterface en función del mapa de interfaz del objeto. Es necesario convertir el valor devuelto a un valor HRESULT. Si el objeto es agregado, esta función llamará al “IUnknown de control” en lugar de usar el mapa de interfaz local.

CCmdTarget::ExternalAddRef: descripción de la función

DWORD ExternalAddRef();

Observaciones

Llame a esta función en la implementación de IUnknown::AddRef para cada interfaz que implemente la clase. El valor devuelto es el nuevo recuento de referencias del objeto CCmdTarget. Si el objeto es agregado, esta función llamará al “IUnknown de control” en lugar de manipular el recuento de referencias local.

CCmdTarget::ExternalRelease: Descripción de la función

DWORD ExternalRelease();

Observaciones

Llame a esta función en la implementación de IUnknown::Release para cada interfaz que implemente la clase. El valor devuelto indica el nuevo recuento de referencias en el objeto . Si el objeto es agregado, esta función llamará al “IUnknown de control” en lugar de manipular el recuento de referencias local.

DECLARE_INTERFACE_MAP: descripción de la macro

DECLARE_INTERFACE_MAP

Observaciones

Use esta macro en cualquier clase derivada de CCmdTarget que tenga un mapa de interfaz. Se usa de la misma manera que DECLARE_MESSAGE_MAP. Esta invocación de macro debe colocarse en la definición de clase, normalmente en un encabezado (. Archivo H). Una clase con DECLARE_INTERFACE_MAP debe definir el mapa de interfaz en el archivo de implementación (. CPP) con las macros BEGIN_INTERFACE_MAP y END_INTERFACE_MAP.

BEGIN_INTERFACE_PART y END_INTERFACE_PART: descripciones de macros

BEGIN_INTERFACE_PART(localClass, iface);
END_INTERFACE_PART(localClass)

Parámetros

localClass
Nombre de la clase que implementa la interfaz

iface
Nombre de la interfaz que implementa esta clase.

Observaciones

Por cada interfaz que su clase implementa, debe tener un par de BEGIN_INTERFACE_PART y END_INTERFACE_PART. Estas macros definen una clase local derivada de la interfaz OLE que se define, así como una variable de miembro incrustada de esa clase. Los miembros AddRef, Release y QueryInterface se declaran automáticamente. Debe incluir las declaraciones de las otras funciones miembro que forman parte de la interfaz que se implementa (esas declaraciones se colocan entre las macros BEGIN_INTERFACE_PART y END_INTERFACE_PART).

El argumento iface es la interfaz OLE que desea implementar, como IAdviseSink o IPersistStorage (o su propia interfaz personalizada).

El argumento localClass es el nombre de la clase local que se definirá. Una “X” se antepone al nombre automáticamente. Esta convención de nomenclatura se usa para evitar colisiones con clases globales con el mismo nombre. Además, el nombre del miembro incrustado es igual que el nombre de la clase local , excepto que está precedido por "m_x".

Por ejemplo:

BEGIN_INTERFACE_PART(MyAdviseSink, IAdviseSink)
    STDMETHOD_(void, OnDataChange)(LPFORMATETC, LPSTGMEDIUM);
    STDMETHOD_(void, OnViewChange)(DWORD, LONG);
    STDMETHOD_(void, OnRename)(LPMONIKER);
    STDMETHOD_(void, OnSave)();
    STDMETHOD_(void, OnClose)();
END_INTERFACE_PART(MyAdviseSink)

definiría una clase local denominada XMyAdviseSink derivada de IAdviseSink y un miembro de la clase en la que se declara m_xMyAdviseSink.Note:

Nota

Las líneas que comienzan con STDMETHOD_ se copian esencialmente de OLE2.H y modificadas ligeramente. Copiarlos desde OLE2. H puede reducir los errores difíciles de resolver.

BEGIN_INTERFACE_MAP y END_INTERFACE_MAP: descripciones de macros

BEGIN_INTERFACE_MAP(theClass, baseClass)
END_INTERFACE_MAP

Parámetros

theClass
Clase en la que se va a definir el mapa de interfaz

baseClass
La clase de la que theClass se deriva.

Observaciones

Las macros BEGIN_INTERFACE_MAP y END_INTERFACE_MAP se usan en el archivo de implementación para definir realmente el mapa de interfaz. Para cada interfaz que se implementa hay una o varias INTERFACE_PART invocaciones de macro. Para cada agregado que usa la clase, hay una invocación de macro INTERFACE_AGGREGATE.

INTERFACE_PART: descripción de macros

INTERFACE_PART(theClass, iid, localClass)

Parámetros

theClass
Nombre de la clase que contiene el mapa de interfaz.

iid
El IID que se debe asignar a la clase incrustada.

localClass
Nombre de la clase local (menos la "X").

Observaciones

Esta macro se usa entre la macro BEGIN_INTERFACE_MAP y la macro END_INTERFACE_MAP para cada interfaz que admitirá el objeto. Permite asignar un IID a un miembro de la clase indicada por theClass y localClass. "m_x" se agregará automáticamente al localClass. Tenga en cuenta que puede que haya más de un IID asociado a un único miembro. Esto es muy útil cuando se implementa solo una interfaz "más derivada" y se desea proporcionar también todas las interfaces intermedias. Un buen ejemplo de esto es la interfaz IOleInPlaceFrameWindow. Su jerarquía tiene este aspecto:

IUnknown
    IOleWindow
        IOleUIWindow
            IOleInPlaceFrameWindow

Si un objeto implementa IOleInPlaceFrameWindow, un cliente puede QueryInterface en cualquiera de estas interfaces: IOleUIWindow, IOleWindow o IUnknown, además de la interfaz “más derivada” IOleInPlaceFrameWindow (la que esté realmente implementando). Para controlar esto, puede usar más de una macro de INTERFACE_PART para asignar cada interfaz base a la interfaz de IOleInPlaceFrameWindow:

en el archivo de definición de clase:

BEGIN_INTERFACE_PART(CMyFrameWindow, IOleInPlaceFrameWindow)

en el archivo de implementación de clase:

BEGIN_INTERFACE_MAP(CMyWnd, CFrameWnd)
    INTERFACE_PART(CMyWnd, IID_IOleWindow, MyFrameWindow)
    INTERFACE_PART(CMyWnd, IID_IOleUIWindow, MyFrameWindow)
    INTERFACE_PART(CMyWnd, IID_IOleInPlaceFrameWindow, MyFrameWindow)
END_INTERFACE_MAP

El marco de trabajo se encarga de IUnknown porque siempre se requiere.

INTERFACE_PART: descripción de macros

INTERFACE_AGGREGATE(theClass, theAggr)

Parámetros

theClass
Nombre de la clase que contiene el mapa de interfaz,

theAggr
Nombre de la variable miembro que se va a agregar.

Observaciones

Esta macro se usa para indicar al marco de trabajo que la clase usa un objeto agregado. Debe aparecer entre las macros BEGIN_INTERFACE_PART y END_INTERFACE_PART. Un objeto agregado es un objeto independiente, derivado de IUnknown. Mediante el uso de un agregado y la macro INTERFACE_AGGREGATE, puede hacer que todas las interfaces que admite el agregado parezcan ser compatibles directamente con el objeto . El argumento theAggr es simplemente el nombre de una variable miembro de tu clase que se deriva de IUnknown (ya sea directamente o indirectamente). Todas las macros INTERFACE_AGGREGATE deben seguir las macros de INTERFACE_PART cuando se colocan en un mapa de interfaz.

Consulte también

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