Exportar (0) Imprimir
Expandir todo
Expandir Minimizar
Este artículo proviene de un motor de traducción automática. Mueva el puntero sobre las frases del artículo para ver el texto original. Más información.
Traducción
Original

TN038: Implementación de IUnknown en MFC/OLE

Nota 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.

En el núcleo de OLE 2 está el "Modelo de objetos componentes OLE" o COM. COM define un estándar para la comunicación entre objetos que cooperen. Esto incluye los detalles del aspecto de un “objeto”, por ejemplo, cómo se envían los métodos en un objeto. COM también define una clase base, de la que se derivan todas las clases compatibles COM. Esta clase base es IUnknown. Aunque la interfaz IUnknown se conoce como una clase de C++, COM no es específico de ningún lenguaje: se puede implementar en C, PASCAL o cualquier otro lenguaje que admita el diseño binario de un objeto COM.

OLE hace referencia a todas las clases derivadas de IUnknown como “interfaces”. Esta distinción es importante, ya que una “interfaz” como IUnknown no lleva ninguna implementación. Define simplemente el protocolo mediante el cual los objetos se comunican, no los detalles de lo que hacen esas implementaciones. Esto es razonable para un sistema que permite la máxima flexibilidad. Es tarea de MFC implementar un comportamiento predeterminado para programas de MFC/C++.

Para entender la implementación de MFC de IUnknown, primero debe entender qué 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;
};
NotaNota

Ciertos detalles necesarios de convención de llamada, como __stdcall, quedan fuera de esta ilustración.

Las funciones miembro AddRef y Release controlan la administración de memoria del objeto. COM utiliza un esquema de recuento de referencias para el seguimiento de objetos. Nunca se hace referencia directamente a un objeto como 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 ha terminado de usarlo, se llama primero al miembro Release del objeto (en oposición a utilizar el operador delete, como se hace para un objeto tradicional de C++). El mecanismo de recuento de referencias permite varias referencias a un objeto único que se va a administrar. Una implementación AddRef y Versión mantiene un recuento de referencias en el objeto (el objeto no se elimina hasta que el recuento de referencias llega a cero).

AddRef y Versión son bastante directos desde el punto de vista de la implementación. A continuación se muestra 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 algo más interesante. No resulta muy interesante tener un objeto cuyas únicas funciones de miembro son AddRef y Release; sería excelente indicar al objeto que haga más cosas que las proporcionadas por IUnknown. Aquí es donde QueryInterface es útil. Le permite obtener otra “interfaz” en el mismo objeto. Estas interfaces se suelen derivar de IUnknown y aportan funcionalidad adicional agregando nuevas funciones miembro. Las interfaces COM nunca tienen variables de miembro declaradas en la interfaz y todas las funciones de miembro se declaran como virtuales puras. Por ejemplo,

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

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

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

Parece muy sencillo, pero ¿cómo implementaría un objeto que admita tanto la interfaz IPrintInterface como IUnknown? En este caso es simple porque 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 igual que las implementadas anteriormente. CPrintObj::QueryInterface presentará el siguiente aspecto:

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; si no, se produce un error. Tenga en cuenta también que una QueryInterface correcta da lugar a una AddRef implícita. Por supuesto, también debería tener que implementar CEditObj::Print. Esto es simple, porque IPrintInterface se derivó directamente de la interfaz IUnknown. Sin embargo, si desea admitir dos interfaces diferentes, ambas derivadas de IUnknown, considera lo siguiente:

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

Aunque hay varias maneras de implementar una clase que admita tanto IEditInterface como 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;
};

La implementación completa se incluye a continuació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 para recuperar cualquier interfaz que el objeto pueda admitir y desde cada una de esas interfaces es posible hacer lo mismo. Esto significa que todas las funciones QueryInterface disponibles en cada interfaz deben comportarse exactamente la misma manera. Para que estos objetos incrustados llamen a la implementación del “objeto externo”, se utiliza un puntero alternativo (m_pParent). El puntero m_pParent se inicializa durante el constructor de CEditPrintObj. A continuación implementaría CEditPrintObj::CPrintObj::PrintObject y CEditPrintObj::CEditObj::EditObject también. Se agregó bastante código para agregar una característica: la posibilidad de editar el objeto. Afortunadamente, es bastante raro que las interfaces tengan una sola función miembro (aunque puede ocurrir) y en este caso, EditObject y PrintObject estarán combinados normalmente en una única interfaz.

Es una explicación muy extensa y demasiado código para un escenario tan simple. Las clases de MFC/OLE proporcionan una alternativa más sencilla. La implementación de MFC utiliza una técnica similar a la manera en que los mensajes de Windows se ajustan con los mapas de mensajes. Esta función se denomina Mapas de interfaz y se explica en la sección siguiente.

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

  • Una implementación estándar IUnknown, integrada en la clase CCmdTarget .

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

  • Implementación controlada por datos de QueryInterface

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

  • Compatibilidad para crear objetos COM que se pueden agregar

  • Compatibilidad para utilizar objetos de agregado en la implementación de un objeto COM

  • La implementación se puede enlazar y es extensible

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

La compatibilidad de los mapas de interfaz de MFC cuya raíz comienza en la clase CCmdTarget. La cuenta de referencia CCmdTarget "has-a" así como todas las funciones de miembro asociadas con la implementación IUnknown (la cuenta de referencia por ejemplo está en CCmdTarget). Para crear una clase que admita COM OLE, derive una clase CCmdTarget y use distintas macros así como funciones miembro CCmdTarget para implementar las interfaces deseadas. La implementación de MFC utiliza clases anidadas para definir cada implementación de interfaz de una forma muy parecida al ejemplo anterior. Esto se facilita con una implementación estándar de IUnknown, así como varias macros que eliminan parte del código repetitivo.

Para implementar una clase mediante los 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 desee admitir, utilice 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 que admita.

  7. Utilice la macro METHOD_PROLOGUE para tener acceso al elemento primario, un objeto derivado de CCmdTarget.

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

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

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 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 los nombres CEditObj y CPrintObj (la X se usa únicamente para diferenciar las clases anidadas de las clases globales que comienzan con "C" y de las clases de interfaz que empiezan por "I"). Los dos miembros anidados de estas clases son created: m_CEditObj y m_CPrintObj, respectivamente. Las macros declaran automáticamente las funciones AddRef, Release y QueryInterface ; por consiguiente solo declara las funciones concretas para esta interfaz: EditObject y PrintObject (se utiliza STDMETHOD de manera que _stdcall y las palabras clave virtuales se proporcionan como adecuadas 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 e IID_IEditInterface con m_CEditObj respectivamente. La implementación CCmdTarget de QueryInterface (CCmdTarget::ExternalQueryInterface) utiliza este mapa para devolver punteros a m_CPrintObj y m_CEditObj cuando se solicite. No es necesario incluir una entrada para IID_IUnknown; el marco de trabajo utilizará la primera interfaz en el mapa (en este caso, m_CPrintObj) al solicitarse IID_IUnknown.

Aunque la macro BEGIN_INTERFACE_PART declaró las funciones AddRef, Release y QueryInterface automáticamente, todavía 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 para CEditPrintObj::CPrintObj, sería similar a las definiciones anteriores para CEditPrintObj::CEditObj. Aunque sería posible crear una macro que podría utilizarse para generar automáticamente estas funciones (antes en el desarrollo de MFC/OLE era así), se hace difícil establecer puntos de interrupción cuando una macro genera más de una línea de código. Por esta razón, este código se expande manualmente.

Mediante la implementación de marco de trabajo de los mapas de mensajes, hay varias cosas que no es necesario hacer:

  • Implementar QueryInterface

  • Implementar AddRef y Release

  • Declarar cualquiera de estos métodos integrados en las dos interfaces

Además, el marco de trabajo usa mapas de mensajes internamente. Esto permite derivar de una clase de marco, por ejemplo, COleServerDoc, que admite algunas interfaces y proporciona reemplazos o incorporaciones a las interfaces proporcionadas por el marco. Puede hacerlo porque el marco admite totalmente la herencia de una asignación de interfaz de una clase base. Esta es la razón por la que BEGIN_INTERFACE_MAP toma como segundo parámetro el nombre de la clase base.

NotaNota

Suele no ser posible reutilizar la implementación de implementaciones integradas de MFC de las interfaces OLE simplemente heredando la especialización incrustada de esa interfaz de la versión de MFC. Esto no es posible porque el uso de la macro METHOD_PROLOGUE para obtener acceso al objeto derivado CCmdTargetque la contiene implica un desplazamiento fijo del objeto incrustado desde el objeto derivado CCmdTarget. Esto significa, por ejemplo, que no puede derivar un XMyAdviseSink incrustado de la implementación de MFC en COleClientItem::XAdviseSink, porque XAdviseSink se basa en estar en un desplazamiento concreto desde la parte superior del objeto COleClientItem.

NotaNota

Puede, sin embargo, delegar a la implementación de MFC todas las funciones para las que desea el comportamiento predeterminado de MFC. Esto se hace en la implementación de MFC de IOleInPlaceFrame (XOleInPlaceFrame) en la clase COleFrameHook (delega en m_xOleInPlaceUIWindow para muchas funciones). Este diseño se eligió para reducir el tamaño del runtime de objetos que implementan muchas interfaces; elimina la necesidad de un puntero adicional (como la manera en que m_pParent se utilizó en la sección anterior).

5hhehwba.collapse_all(es-es,VS.120).gifMapas de agregación y de interfaz

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

Hay dos formas de utilizar la agregación: (1) mediante un objeto COM compatible con la agregación y (2) implementando un objeto que otro puede agregar. Se puede hacer referencia a estas capacidades como “utilizar un objeto global” y “crear un objeto que se puede agregar”. MFC admite ambos.

5hhehwba.collapse_all(es-es,VS.120).gifUso de un objeto agregado

Para utilizar un objeto agregado, es necesaria alguna manera de asociar el agregado en el mecanismo QueryInterface. Es decir, el objeto agregado debe comportarse como si fuera una parte nativa del objeto. Así pues, ¿cómo se asocia al mecanismo de asignación de interfaz de MFC? Además de la macro INTERFACE_PART, donde un objeto anidado se asigna a un identificador IID, también puede declarar un objeto agregado como parte de la clase derivada CCmdTarget. Para ello, se usa la macro INTERFACE_AGGREGATE. Esto permite especificar una variable miembro (que debe ser un puntero a IUnknown o a una clase derivada), que debe estar integrada en el mecanismo de asignación de interfaz. Si el puntero no es NULL cuando se llama a CCmdTarget::ExternalQueryInterface, el marco automáticamente llamará 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 utilizar la macro INTERFACE_AGGREGATE

  1. Declare una variable miembro ( IUnknown*) que contiene 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 una buena ubicación para crear sus objetos de agregado. Tendrá que llamar explícitamente si crea el objeto fuera de la implementación de MFC de COleObjectFactory. La razón para crear agregados en CCmdTarget::OnCreateAggregates así como el uso de CCmdTarget::GetControllingUnknown será aparente cuando se describe la creación de objetos que se pueden agregar.

Esta técnica ofrecerá al objeto todas las interfaces que el objeto agregado admita más sus interfaces nativas. Si solo desea un subconjunto de las interfaces que admite el agregado, puede reemplazar CCmdTarget::GetInterfaceHook. Esto aporta una capacidad de enlace de muy bajo nivel, similar a QueryInterface. Generalmente, desea todas las interfaces que el agregado admite.

5hhehwba.collapse_all(es-es,VS.120).gifHacer que se pueda agregar una implementación de objeto

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

Si además el objeto utiliza agregados, también debe estar seguro de pasar el "controlar lo desconocido" correcto a los objetos de agregado. Este puntero IUnknown se pasa normalmente al objeto cuando se crea el agregado. Por ejemplo, el parámetro pUnkOuter es el "controlar lo desconocido" para objetos creados con CoCreateInstance. El puntero correcto para "controlar lo desconocido" se puede recuperar llamando a CCmdTarget::GetControllingUnknown. El valor devuelto de dicha función; sin embargo, no es válido durante el constructor. Por este motivo, se sugiere que cree los agregados solo en una invalidación de CCmdTarget::OnCreateAggregates, donde el valor devuelto de GetControllingUnknown es confiable, aunque se haya creado a partir de la implementación 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 sea así, llame siempre a ExternalAddRef y ExternalRelease en lugar de InternalRelease y InternalAddRef. Es raro llamar a InternalRelease o InternalAddRef en una clase que admite agregación.

5hhehwba.collapse_all(es-es,VS.120).gifMaterial de referencia

El uso avanzado de OLE, tal como la definición de sus propias interfaces o la invalidación de la implementación de las interfaces OLE requiere el uso del mecanismo subyacente de asignación de interfaz.

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

5hhehwba.collapse_all(es-es,VS.120).gifCCmdTarget::EnableAggregation — Descripción de la función

void EnableAggregation();

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

5hhehwba.collapse_all(es-es,VS.120).gifCCmdTarget::ExternalQueryInterface — Descripción de la función

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

5hhehwba.collapse_all(es-es,VS.120).gifParámetros

lpIID

Puntero lejano un IID (el primer argumento para QueryInterface)

ppvObj

Puntero a un IUnknown* (segundo argumento para QueryInterface)

Llame a esta función en su 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 según la asignación de la interfaz del objeto. Es necesario convertir el valor devuelto en HRESULT. Si se agrega el objeto, esta función llamará a "controlar IUnknown" en lugar de usar el mapa de la interfaz local.

5hhehwba.collapse_all(es-es,VS.120).gifCCmdTarget::ExternalAddRef — Descripción de la función

DWORD ExternalAddRef();

Llame a esta función en su implementación de IUnknown::AddRef para cada interfaz que implemente la clase. El valor devuelto es el nuevo número de referencia nuevo en el objeto CCmdTarget. Si se agrega el objeto, esta función llamará a "controlar IUnknown" en lugar de manipular el recuento local de referencia.

5hhehwba.collapse_all(es-es,VS.120).gifCCmdTarget::ExternalRelease — Descripción de la función

DWORD ExternalRelease();

Llame a esta función en su implementación de IUnknown::Release para cada interfaz que implemente la clase. El valor devuelto indica el nuevo número de referencias en el objeto. Si se agrega el objeto, esta función llamará a "controlar IUnknown" en lugar de manipular el recuento local de referencia.

5hhehwba.collapse_all(es-es,VS.120).gifDECLARE_INTERFACE_MAP — Descripción de macro

DECLARE_INTERFACE_MAP

Use esta macro en cualquier clase derivada de CCmdTarget que tendrá un mapa de interfaz. Se usa de la misma manera que DECLARE_MESSAGE_MAP. Esta llamada de macro se debe colocar en la definición de clase, normalmente con un archivo de encabezado (.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 .

5hhehwba.collapse_all(es-es,VS.120).gifBEGIN_INTERFACE_PART y END_INTERFACE_PART — descripciones de macro

BEGIN_INTERFACE_PART( 
   localClass,
   iface 
);
END_INTERFACE_PART( 
   localClass 
)

5hhehwba.collapse_all(es-es,VS.120).gifParámetros

localClass

Nombre de la clase que implementa la interfaz

iface

Nombre de la interfaz que implementa esta clase

Para cada interfaz que la clase va a implementar, necesita tener un par BEGIN_INTERFACE_PART y END_INTERFACE_PART. Estas macros definen una clase local derivada de la interfaz OLE que defina, así como una variable 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 está implementando (estas 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á. Automáticamente, se inserta una "X" al principio del nombre. Esta convención de nomenclatura se usa para evitar conflictos con las clases globales del mismo nombre. Además, el nombre del miembro incrustado, igual que el nombre localClass a menos que vaya 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 está declarado llamada m_xMyAdviseSink. Nota:

NotaNota

Las líneas que comienzan por STDMETHOD_ se copian esencialmente de OLE2.H y se modifican ligeramente. Copiarlas de OLE2.H pueden reducir los errores que son difíciles de resolver.

5hhehwba.collapse_all(es-es,VS.120).gifBEGIN_INTERFACE_MAP y END_INTERFACE_MAP — descripciones de macro

BEGIN_INTERFACE_MAP( 
   theClass,
   baseClass 
)
END_INTERFACE_MAP

5hhehwba.collapse_all(es-es,VS.120).gifParámetros

theClass

La clase en la que se va a definir el mapa de interfaz

baseClass

La clase de la que se deriva theClass.

Las macros BEGIN_INTERFACE_MAP y END_INTERFACE_MAP se utilizan en el archivo de implementación para definir el mapa de interfaz en sí. Para cada interfaz que se implementa que hay una o varias llamadas de macro INTERFACE_PART. Para cada agregado que la clase utiliza, hay una llamada de macro INTERFACE_AGGREGATE.

5hhehwba.collapse_all(es-es,VS.120).gifINTERFACE_PART — Descripción de macro

INTERFACE_PART( 
   theClass,
   iid, 
   localClass 
)

5hhehwba.collapse_all(es-es,VS.120).gifParámetros

theClass

Nombre de la clase que contiene el mapa de interfaz.

iid

IID que se va a asignar a la clase incrustada.

localClass

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

Esta macro se utiliza entre la macro BEGIN_INTERFACE_MAP y la macro END_INTERFACE_MAP para cada interfaz que el objeto admitirá. Permite asignar un identificador IID a un miembro de la clase indicada por theClass y localClass. El “m_x” se agregará en localClass automáticamente. Observe que más de un IID se puede asociar a un solo miembro. Resulta muy útil cuando está implementando una sola interfaz “más derivada” y desea proporcionar todas las interfaces intermedias también. 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 en realidad se está implementando). Para administrar esto puede utilizar más de una macro INTERFACE_PART para asignar todas y cada una de las interfaces base a la interfaz 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 se encarga de IUnknown porque se requiere siempre.

5hhehwba.collapse_all(es-es,VS.120).gifINTERFACE_PART — Descripción de macro

INTERFACE_AGGREGATE( 
   theClass,
   theAggr 
)

5hhehwba.collapse_all(es-es,VS.120).gifParámetros

theClass

Nombre de la clase que contiene el mapa de interfaz.

theAggr

Nombre de la variable miembro que se va a agregar.

Esta macro se utiliza para indicar al marco que la clase está utilizando un objeto agregado. Debe aparecer entre BEGIN_INTERFACE_PART y las macros END_INTERFACE_PART. Un objeto agregado es un objeto independiente, derivado de IUnknown. Usando un agregado y la macro INTERFACE_AGGREGATE, puede hacer que parezca que todas las interfaces que admite el agregado las admite directamente el objeto. El argumento theAggr es simplemente el nombre de una variable miembro de la clase que se deriva de IUnknown (directa o indirectamente). Todas las macros INTERFACE_AGGREGATE deben seguir las macros INTERFACE_PART cuando se colocan en un mapa de interfaz.

Adiciones de comunidad

AGREGAR
Mostrar:
© 2014 Microsoft