TN038: MFC/OLE IUnknown attuazione

Nel cuore di OLE 2 è il "OLE Component Object Model", o com. COM definisce uno standard per gli oggetti come cooperanti comunicare l'uno a altro. Ciò include i dettagli di ciò che un "oggetto" assomiglia, tra cui come metodi sono spediti su un oggetto. COM definisce anche una classe di base, da cui derivano tutte le classi compatibile COM. Questa classe di base è IUnknown. Anche se l'interfaccia IUnknown è indicato come una classe C++, COM non è specifico per ogni una lingua — può essere attuato in C, PASCAL o qualsiasi altro linguaggio che può sostenere il binario layout di un oggetto COM.

OLE si riferisce a tutte le classi derivate da IUnknown come "interfaces." Questo è un'importante distinzione, poiché una "interfaccia" come IUnknown non porta con sé alcuna implementazione. Esso definisce semplicemente il protocollo con il quale comunicare gli oggetti, non le specifiche di cosa fanno queste implementazioni. Questo è ragionevole per un sistema che consente la massima flessibilità. È il lavoro di MFC per implementare un comportamento predefinito per i programmi MFC/C++.

Per capire l'implementazione di MFC di IUnknown deve prima capire che cosa è questa interfaccia. Una versione semplificata del IUnknown è definita in seguito:

 classe IUnknown
{
pubblica:
 nbsp;  Virtual HRESULT QueryInterface(REFIID iid, void** ppvObj) = 0;
    Virtual ULO&NG AddRef = 0;
    Virtual ULONG errato = 0;
}

&Notanbsp;  Certo necessari dettagli convenzione chiamante, ad esempio StdCall sono lasciati fuori per questa illustrazione.

Le funzioni membro di AddRef e Release di controllare la gestione della memoria dell'oggetto. COM utilizza uno schema di conteggio di riferimento per tenere traccia degli oggetti. Un oggetto non è mai fatto riferimento direttamente come si farebbe in C++. Invece, oggetti COM vengono sempre fatto riferimento tramite un puntatore. Per rilasciare l'oggetto quando il proprietario è fatto usando, membro di rilascio dell'oggetto viene chiamato (in contrapposizione a utilizzando l'operatore delete, come avverrebbe per un oggetto C++ tradizionale). Il meccanismo nel conteggio dei riferimenti consente più riferimenti a un singolo oggetto deve essere gestito. Un'implementazione di AddRef e Release mantiene un conteggio dei riferimenti dell'oggetto — l'oggetto non viene eliminato fino a quando il conteggio raggiunge lo zero.

AddRef e Release sono abbastanza semplice da un punto di vista di implementazione. Qui è un'implementazione banale:

ULO&NG CMyObj::AddRef() {nbsp;  ritorno + + m_dwRef; 
}

ULONG CMyObj::Release() {se (-m_dwRef = = 0) {
        eliminare questo; 
        return 0;
    }
    return m_dwRef;
}

La funzione membro QueryInterface è un po' più interessante. Come potete immaginare, non è molto interessante avere un oggetto di cui solo le funzioni membro sono AddRef e Release — sarebbe bello indicare l'oggetto per fare le cose più quanto fornisce IUnknown . Questo è dove QueryInterface è utile. Consente di ottenere un diverso "interfaccia" sullo stesso oggetto. Queste interfacce sono solitamente derivate da IUnknown e aggiungono ulteriori funzionalità aggiungendo nuove funzioni membro. Interfacce COM non hanno mai variabili membro dichiarate nell'interfaccia e tutte le funzioni membro vengono dichiarate come puro virtuale. Ad esempio,

classe IPri&ntInterface: IUnknown pubblica
{
pubblica:
 nbsp;  virtual void PrintObject() = 0;
}

Per ottenere un IPrintInterface se avete solo un IUnknown, chiamare IUnknown:: QueryInterface utilizzando l' IID della IPrintInterface. Un IID è un numero di 128-bit che identifica in modo univoco l'interfaccia. C'è un IID per ogni interfaccia che definiscono i si o OLE. Se pUnk è un puntatore a un oggetto IUnknown , potrebbe essere il codice per recuperare un IPrintInterface da esso

IPrintInterface * pPrint = NULL;
Se (pUnk-gt;QueryInterface(IID_IPrintInterface, (void**) & pPrint) = = NOERROR)
{
    pPrint - > PrintObject();
    pPrint - > errato;   
        / / rilascio puntatore ottenuto tramite QueryInterface
}

Che sembra abbastanza facile, ma sarebbe come implementare un oggetto che supporta sia il IPrintInterface e IUnknown interfaccia? In questo caso è semplice, dal momento che il IPrintInterface è derivato direttamente dalla IUnknown — implementando IPrintInterface, IUnknown è automaticamente supportato. Ad esempio:

 classe CPrintObj: pubblico CPrintInterface
{
 nbsp;  Virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
    Virtual ULO&NG AddRef;
    Virtual ULONG errato;
    virtual void PrintObject();
}

Le implementazioni di AddRef e Release sarebbe esattamente le stesse di quelle attuate di sopra. CPrintObj::QueryInterface sembrerebbe qualcosa di simile

HRESULT CPrintObj::QueryInterface(REFIID iid, void FAR* FAR* ppvObj)
{
 nbsp;  Se (iid = = IID_IUnknown | | iid = = IID_IPrintInterface)
    {
        * ppvObj = questo;
        AddRef;
        return &NOERROR;
    }
    return ResultFromScode(E_NOINTERFACE);
}

Come potete vedere, se l'identificatore di interfaccia (IID) viene riconosciuto, restituito un puntatore all'oggetto; in caso contrario si verifica un errore. Si noti inoltre che un successo QueryInterface si traduce in un' implicita AddRef. Naturalmente, si avrebbe anche implementare CEditObj::Print. Che è semplice perché il IPrintInterface è stato derivato direttamente dall'interfaccia IUnknown . Tuttavia, se si voleva sostenere due diverse interfacce, sia derivata da IUnknown, considerare quanto segue

classe IEditI&nterface: pubblico IUnkown
{
pubblica:
 nbsp;  virtual void EditObject() = 0;
}

Anche se ci sono un certo numero di modi diversi di implementare una classe di supporto sia IEditInterface che IPrintInterface, compreso l'uso di C++ ereditarietà multipla, questa nota si concentrerà sull'uso delle classi nidificate per implementare questa funzionalità.

classe CEditPrintObj
{
pubblica:
 nbsp;  CEditPrintObj();

HRESULT QueryInterface(REFIID iid, void**);
    ULO&NG AddRef;
    ULONG errato;
    DWORD m_dwRef;

classe CPrintObj: pubblico IPrintInterface
    {
    pubblica:
        CEditPrintObj * m_pParent;
        Virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
        Virtual ULONG AddRef;
        Virtual ULONG errato;
    } m_printObj;

classe CEditObj: pubblico IEditInterface
    {
    pubblica:
        CEditPrintObj * m_pParent;
        Virtual ULONG QueryInterface(REFIID iid, void** ppvObj);
        Virtual ULONG AddRef;
        Virtual ULONG errato;
    } m_editObj;
}

L'intera implementazione è incluso qui sotto:

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

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

CEditPrintObj::Release()
{
    Se (-m_dwRef = = 0)
    {
        eliminare questo;
        return 0;
    }
    return m_dwRef;
}

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

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

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

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

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

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

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

Si noti che la maggior parte dell'implementazione IUnknown viene inserito nella classe CEditPrintObj , anziché duplicare il codice in CEditPrintObj::CEditObj e CEditPrintObj::CPrintObj. Questo riduce la quantità di codice ed evita i bug. Qui il punto chiave è che dall'interfaccia IUnknown è possibile chiamare QueryInterface per recuperare qualsiasi interfaccia che dell'oggetto potrebbe supportare e da ciascuna di queste interfacce è possibile fare la stessa cosa. Questo significa che tutte le funzioni di QueryInterface disponibile da ogni interfaccia devono comportarsi esattamente allo stesso modo. Al fine di questi oggetti incorporati di chiamare l'implementazione dell'oggetto"esterno", un puntatore posteriore è usato (m_pParent). Il puntatore m_pParent viene inizializzato durante il costruttore CEditPrintObj. Poi si attuerebbe CEditPrintObj::CPrintObj::PrintObject e CEditPrintObj::CEditObj::EditObject pure. Un bel po' di codice è stato aggiunto per aggiungere una caratteristica — la possibilità di modificare l'oggetto. Fortunatamente, è abbastanza raro per le interfacce di avere solo una funzione membro singolo (anche se accadere) e in questo caso, EditObject e PrintObject sarebbe di solito essere combinati in un'unica interfaccia.

Questo è un sacco di spiegazione e un sacco di codice per un tale scenario semplice. Le classi MFC/OLE forniscono un'alternativa più semplice. L'implementazione MFC utilizza una tecnica simile al modo in cui i messaggi di Windows vengono avvolti con mappe messaggi. Questa struttura viene chiamata Mappe di interfaccia e viene discusso nella sezione successiva.

Mappe di interfaccia di MFC

MFC/OLE inclusa l'implementazione di "Interfaccia mappe" simile a "Mappe messaggi" e "Dispatch Maps" di MFC nel concetto e l'esecuzione. Le caratteristiche principali di mappe di interfaccia di MFC sono come segue:

Inoltre, mappe di interfaccia supportano le seguenti funzionalità avanzate:

Per ulteriori informazioni sull'aggregazione, vedere riferimento di OLE Programmer.

Supporto della mappa di MFC interfaccia è radicato nella classe CCmdTarget . CCmdTarget "ha un" riferimento conte, come pure tutte le funzioni di membro associate con l'implementazione di IUnknown (il conteggio dei riferimenti per esempio è in CCmdTarget). Per creare una classe che supporta OLE COM, si derivare una classe da CCmdTarget e utilizzare le macro di vari come funzioni membro di CCmdTarget per implementare le interfacce desiderate. Implementazione di MFC utilizza le classi nidificate per definire ogni implementazione dell'interfaccia molto simile dell'esempio precedente. Ciò è reso più facile con un'implementazione standard di IUnknown, nonché una serie di macro che eliminare alcuni dei codice ripetitivo.

Interfaccia mappa Basics

Per implementare una classe che utilizza l'interfaccia di MFC mappe attenersi alla seguente procedura:

  1. Derivare una classe direttamente o indirettamente da CCmdTarget.

  2. Utilizzare la funzione DECLARE_INTERFACE_MAP nella definizione della classe derivata.

  3. Per ogni interfaccia desideri sostenere, utilizzare le macro BEGIN_INTERFACE_PART ed END_INTERFACE_PART nella definizione della classe.

  4. Nel file di implementazione, utilizzare le macro BEGIN_INTERFACE_MAP ed END_INTERFACE_MAP per definire la mappa dell'interfaccia della classe.

  5. Per ogni IID supportata, utilizzare la macro INTERFACE_PART tra le macro BEGIN_INTERFACE_MAP ed END_INTERFACE_MAP per associare tale IID a uno specifico "parte" della vostra classe.

  6. Implementare ciascuna delle classi nidificate che rappresentano le interfacce che supportano.

  7. Utilizzare la macro METHOD_PROLOGUE per accedere al genitore, CCmdTarget-oggetto derivato.

  8. AddRef, Releasee QueryInterface possono delegare all'implementazione di queste funzioni (ExternalAddRef, ExternalReleaseed ExternalQueryInterface) CCmdTarget.

CPrintEditObj esempio precedente possa essere implementata come segue:

classe CPrintEditObj: CCmdTarget pubblica
{
pubblica:
 nbsp;  / / membro dati e funzioni membro per CPrintEditObj vanno qui

/ / Mappe di interfaccia
protetto:
    DECLARE_I&NTERFACE_MAP()

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

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

La dichiarazione di cui sopra crea una classe derivata da CCmdTarget. La macro DECLARE_INTERFACE_MAP racconta il quadro che questa classe avrà una mappa dell'interfaccia personalizzata. Inoltre, le macro BEGIN_INTERFACE_PART ed END_INTERFACE_PART definiscono classi nidificate, in questo caso con nomi CEditObj e CPrintObj (la x viene utilizzata solo per differenziare le classi nidificate da classi globale che iniziano con "C" e di interfaccia che iniziano con "I"). Vengono creati due membri nidificati di queste classi: m_CEditObj e m_CPrintObj, rispettivamente. Le macro dichiarano automaticamente la AddRef, rilascioe QueryInterface funzioni; è pertanto necessario solo dichiarare le funzioni specifiche di questa interfaccia: EditObject e PrintObject (la macro OLE STDMETHOD è utilizzato tale modo che StdCall e parole chiave virtuale sono fornite come appropriata per la piattaforma di destinazione).

Per implementare la mappa dell'interfaccia per questa classe:

BEGI&N_INTERFACE_MAP (CPrintEditObj, CCmdTarget)
 nbsp;  INTERFACE_PART (CPrintEditObj, IID_IPrintInterface, PrintObj)
    INTERFACE_PART (CPrintEditObj, IID_IEditInterface, EditObj)
END_INTERFACE_MAP()

Questo collega l'IID IID_IPrintInterface con m_CPrintObj e IID_IEditInterface con m_CEditObj rispettivamente. L'implementazione di CCmdTarget di QueryInterface (CCmdTarget::ExternalQueryInterface) utilizza questa mappa per restituire i puntatori a m_CPrintObj e m_CEditObj quando richiesto. Esso non è necessario includere una voce per IID_IUnknown; il framework utilizzerà la prima interfaccia nella mappa (in questo caso m_CPrintObj) quando viene richiesto IID_IUnknown.

Anche se la macro BEGIN_INTERFACE_PART dichiarate automaticamente la AddRef, rilascio e funzioni QueryInterface per te, è comunque necessario implementarle:

ULONG lontano esportazione CEditPrintObj::XEditObj::AddRef()
{
 nbsp;  METHOD_PROLOGUE (CEditPrintObj, EditObj)
    ritorno pThis - > ExternalAddRef();
}

ULONG lontano esportazione CEditPrintObj::XEditObj::Release()
{
    METHOD_PROLOGUE (CEditPrintObj, EditObj)
    ritorno pThis - > ExternalRelease();
}

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

public static void lontano EXPORT CEditPrintObj::XEditObj::EditObject()
{
    METHOD_PROLOGUE (CEditPrintObj, EditObj)
    / / codice per "Edit" l'oggetto, che significa...
}

L'implementazione per CEditPrintObj::CPrintObj, sarebbe simile alle definizioni di cui sopra per CEditPrintObj::CEditObj. Sebbene sia possibile creare una macro che potrebbe essere utilizzata per generare automaticamente queste funzioni (in realtà, all'inizio nello sviluppo MFC/OLE fu il caso), diventa difficile per impostare punti di interruzione, quando una macro genera più di una riga di codice. Per questo motivo, questo codice viene espanso manualmente.

Utilizzando l'implementazione del framework di messaggio mappe ci sono un certo numero di cose che non erano necessarie per fare:

Inoltre, il framework utilizza mappe messaggi internamente. Ciò permette che derivano da una classe di quadro, dire COleServerDoc, che già supporta alcune interfacce e fornisce sostituzioni o aggiunte alle interfacce fornite dal framework. Questa opzione è attivata dal fatto che il framework supporta pienamente che eredita un'interfaccia mappa da una classe base — che è la ragione perché BEGIN_INTERFACE_MAP prende come secondo parametro il nome della classe base.

&Notanbsp;  In generale, non è possibile riutilizzare l'implementazione delle implementazioni incorporate di MFC delle interfacce OLE ereditando la specializzazione incorporata di quell'interfaccia dalla versione MFC. Questo non è possibile perché l'uso della macro METHOD_PROLOGUE per avere accesso alla contenenti CCmdTarget-oggetto derivato implica un fisso di offset dell'oggetto incorporato dalla CCmdTarget-oggetto derivato. Ciò significa, ad esempio, che non può derivare un XMyAdviseSink incorporato dall'implementazione di MFC in COleClientItem::XAdviseSink, poiché XAdviseSink si basa su sta a un offset specifico dalla parte superiore dell'oggetto COleClientItem.

È possibile, tuttavia, delegare all'implementazione MFC per tutte le funzioni che si desidera il comportamento predefinito di MFC. Questo viene fatto nell'implementazione MFC di IOleInPlaceFrame (XOleInPlaceFrame) nella classe COleFrameHook (delega a m_xOleInPlaceUIWindow per molte funzioni). Questo design è stato scelto per ridurre le dimensioni del runtime di oggetti che implementano molte interfacce; Elimina la necessità di un puntatore indietro (come ad esempio il modo m_pParent è stato utilizzato nella sezione precedente).

Aggregazione e mappe di interfaccia

Oltre a supportare gli oggetti COM stand-alone, MFC supporta anche l'aggregazione. Aggregazione di sé è un tema troppo complesso per discutere qui; fare riferimento al riferimento di OLE Programmer per ulteriori informazioni sull'aggregazione. Questa nota semplicemente descriverà il supporto per l'aggregazione integrato nel framework e interfaccia mappe.

Ci sono due modi per utilizzare l'aggregazione: (1) utilizzando un oggetto COM che supporta l'aggregazione e (2) implementazione di un oggetto che può essere aggregato da un altro. Queste capacità possono essere definite come "utilizzando un oggetto aggregato" e "fare un oggetto aggregabili". MFC supporta entrambi.

Utilizzo di un oggetto aggregato

Per utilizzare un oggetto aggregato, ci deve essere qualche modo di legare l'aggregato nel meccanismo di QueryInterface. In altre parole, l'oggetto aggregato deve comportarsi come se fosse una parte nativa dell'oggetto. Così come fa questo legame nel meccanismo di mappa di MFC interfaccia? Oltre alla macro INTERFACE_PART , dove un oggetto nidificato viene mappato a un IID, può anche dichiarare un oggetto aggregato come parte della vostra classe CCmdTarget derivato. A tal fine, viene utilizzata la macro INTERFACE_AGGREGATE . Consente di specificare una variabile membro (che deve essere un puntatore a un IUnknown o la classe derivata), che deve essere integrato nel meccanismo di mappa interfaccia. Se il puntatore non è NULL quando viene chiamato CCmdTarget::ExternalQueryInterface , quadro chiamerà automaticamente la funzione di membro dell'oggetto aggregato QueryInterface , se l' IID richiesto non è uno del nativo s IIDsupportato da CCmdTarget oggetto stesso.

Per utilizzare la macro INTERFACE_AGGREGATE, attenersi alla seguente procedura:

  1. Dichiarare una variabile membro (un IUnknown *) che conterrà un puntatore all'oggetto aggregato.

  2. Includere una macro INTERFACE_AGGREGATE nella mappa interfaccia, che si riferisce alla variabile membro per nome.

  3. Ad un certo punto (di solito durante CCmdTarget::OnCreateAggregates) inizializzare la variabile membro a qualcosa di diverso da NULL.

Ad esempio,

classe CAggrExample: CCmdTarget pubblica
{
pubblica:
 nbsp;  CAggrExample();

protetto:
    LPUNKNOWN m_lpAggrInner;
    Virtual BOOL OnCreateAggregates();

DECLARE_INTERFACE_MAP()
    / / macro parte di interfaccia "nativo" possono essere usate qui
};

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

BOOL CAggrExample::OnCreateAggregates()
{
    / / cablare aggregato con corretto controllo ignoto
    m_lpAggrInner = CoCreateInstance (CLSID_Example,
        GetControllingUnknown, CLSCTX_INPROC_SERVER,
        IID_IUnknown, (LPVOID *) & m_lpAggrInner);
    Se (m_lpAggrInner = = NULL)
        return FALSE;
    / / Facoltativamente, creare altri oggetti aggregati qui
    return TRUE;
}

BEGIN_INTERFACE_MAP (CAggrExample, CCmdTarget)
    / / native voci "INTERFACE_PART" vanno qui
    INTERFACE_AGGREGATE (CAggrExample, m_lpAggrInner)
END_INTERFACE_MAP()

Il m_lpAggrInner viene inizializzata nel costruttore su NULL. Il quadro verrà ignorata una variabile membro NULL nell'implementazione predefinita di QueryInterface. OnCreateAggregates è un buon posto per creare gli oggetti aggregati. Sarà necessario chiamare in modo esplicito se si crea l'oggetto di fuori dell'implementazione MFC di COleObjectFactory. La ragione per la creazione di aggregazioni in CCmdTarget::OnCreateAggregates , nonché l'utilizzo di CCmdTarget::GetControllingUnknown diventerà evidente quando la creazione di oggetti aggregabili è discusso.

Questa tecnica vi darà l'oggetto tutte le interfacce che l'oggetto aggregato supporta inoltre le interfacce native. Se si desidera solo un sottoinsieme delle interfacce che supporta l'aggregazione, si possono eseguire l'override di CCmdTarget::GetInterfaceHook. Ciò permette che hookability di livello molto basso, simili a QueryInterface. Di solito, si desidera che tutte le interfacce che supporta l'aggregato.

Facendo un'implementazione dell'oggetto aggregabile

Per un oggetto di essere aggregabile, l'implementazione di QueryInterface AddRefe Releasedeve delegare a un "controllo sconosciuto". Per essere parte dell'oggetto, in altre parole, essa deve delegare QueryInterface AddRefe Releasea un oggetto diverso, anche derivato da IUnknown. Questo "controllo sconosciuto" è fornito all'oggetto quando viene creato, che è, esso viene fornito all'implementazione di COleObjectFactory. Implementazione di questa porta una piccola quantità di overhead e in alcuni casi non è desiderabile, così MFC rende facoltativo. Per abilitare un oggetto essere aggregabile, chiamare CCmdTarget::EnableAggregation dal costruttore dell'oggetto.

Se l'oggetto viene utilizzato anche aggregati, è inoltre necessario essere sicuri di passare il corretto "controllo sconosciuto" agli oggetti aggregati. Di solito questo puntatore a IUnknown viene passato all'oggetto quando viene creato l'aggregato. Ad esempio, il parametro pUnkOuter è il "controllo sconosciuto" per gli oggetti creati con CoCreateInstance. Il corretto "controllo sconosciuto" puntatore può essere recuperato chiamando CCmdTarget::GetControllingUnknown. Il valore restituito da tale funzione, tuttavia, non è valido durante il costruttore. Per questo motivo, si è suggerito che si crea il tuoi aggregati solo in un override del CCmdTarget::OnCreateAggregates, dove il valore restituito da GetControllingUnknown è affidabile, anche se creato dall'implementazione COleObjectFactory.

È anche importante che l'oggetto manipolare il conteggio dei riferimenti corretti quando si aggiungono o rilasciando i conteggi di riferimento artificiale. Per garantire che questo è il caso, sempre chiamare ExternalAddRef ed ExternalRelease invece di InternalRelease e InternalAddRef. È raro per chiamare InternalRelease o InternalAddRef su una classe che supporta l'aggregazione.

Materiale di riferimento

L'utilizzo avanzato di OLE, ad esempio definendo i proprio interfacce o si esegue l'override di implementazione del framework delle interfacce OLE è necessario utilizzare il meccanismo di mappa sottostante interfaccia.

Questa sezione discute ogni macro e le API che viene utilizzate per implementare queste funzionalità avanzate.

CCmdTarget::EnableAggregation — Funzione descrizione

void EnableAggregation();

&Notanbsp;  Se si desidera supportare l'aggregazione di OLE per gli oggetti di questo tipo, chiamare questa funzione nel costruttore della classe derivata. Questo si prepara una speciale implementazione IUnknown che è necessaria per gli oggetti aggregabili.

CCmdTarget::ExternalQueryInterface — Funzione descrizione

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

lpIID

Puntatore lontano a un IID (il primo argomento QueryInterface)

ppvObj

Puntatore a IUnknown * (secondo argomento per QueryInterface)

&Notanbsp;  Chiamare questa funzione nell'implementazione di IUnknown per ogni interfaccia di classe implementa. Questa funzione fornisce l'implementazione di standard basate sui dati di QueryInterface basato su mappa dell'interfaccia dell'oggetto. È necessario eseguire il cast del valore restituito a un HRESULT. Se l'oggetto viene aggregata, questa funzione chiamerà il "controllo IUnknown" invece di utilizzare la mappa dell'interfaccia locale.

CCmdTarget::ExternalAddRef — Funzione descrizione

DWORD ExternalAddRef();

&Notanbsp;  Chiamare questa funzione nell'implementazione di IUnknown::AddRef per ogni interfaccia di classe implementa. Il valore restituito è il nuovo conteggio dei riferimenti dell'oggetto CCmdTarget. Se l'oggetto viene aggregata, questa funzione chiamerà il "controllo IUnknown" invece di manipolare il conteggio dei riferimenti locali.

CCmdTarget::ExternalRelease — Funzione descrizione

DWORD ExternalRelease();

&Notanbsp;  Chiamare questa funzione nell'implementazione di IUnknown::Release per ogni interfaccia di classe implementa. Il valore restituito indica il nuovo conteggio dei riferimenti dell'oggetto. Se l'oggetto viene aggregata, questa funzione chiamerà il "controllo IUnknown" invece di manipolare il conteggio dei riferimenti locali.

DECLARE_INTERFACE_MAP — Macro descrizione

DECLARE_INTERFACE_MAP

&Notanbsp;  Utilizzare questa macro in qualsiasi classe derivata da CCmdTarget che avrà una mappa dell'interfaccia. Usato più o meno allo stesso modo di DECLARE_MESSAGE_MAP. Questa chiamata di macro deve essere posizionata nella definizione della classe, di solito in un'intestazione (.H) file. Una classe con DECLARE_INTERFACE_MAP necessario definire la mappa dell'interfaccia nel file di implementazione (.CPP) con le macro BEGIN_INTERFACE_MAP ed END_INTERFACE_MAP.

BEGIN_INTERFACE_PART ed END_INTERFACE_PART — descrizioni Macro

BEGIN_INTERFACE_PART (localClass, iface);

END_INTERFACE_PART (localClass)

localClass

Il nome della classe che implementa l'interfaccia

iface

Il nome dell'interfaccia che implementa questa classe

&Notanbsp;  Per ogni interfaccia che implementerà la vostra classe, è necessario disporre di una coppia BEGIN_INTERFACE_PART ed END_INTERFACE_PART . Queste macro definiscono una classe locale derivata dall'interfaccia OLE che definisce come una variabile membro incorporato di tale classe. I membri di AddRefe Release QueryInterface vengono dichiarati automaticamente. È necessario includere le dichiarazioni per le altre funzioni membro che fanno parte dell'interfaccia in corso di attuazione (tali dichiarazioni sono posti tra le macro BEGIN_INTERFACE_PART ed END_INTERFACE_PART ).

L'argomento iface è l'interfaccia OLE che si desidera implementare, ad esempio IAdviseSink, o IPersistStorage (o un'interfaccia personalizzata).

L'argomento localClass è il nome della classe locale che verrà definito. Un «X» sarà automaticamente essere anteposto al nome. Questa convenzione di denominazione è usata per evitare collisioni con le classi globale dello stesso nome. Inoltre, il nome del membro incorporato, il nome del localClass tranne che viene anteposto 'm_x'.

Ad esempio:

BEGI&N_INTERFACE_PART (MyAdviseSink, IAdviseSink)
 nbsp; STDMETHOD_(void,OnDataChange) (LPFORMATETC, LPSTGMEDIUM);
   STDMETHOD_(void,OnViewChange) (DWORD, lunga);
   STDMETHOD_(void,OnRename)(LPMONIKER);
   STDMETHOD_(void,OnSave)();
   STDMETHOD_(void,OnClose)();
END_INTERFACE_PART(MyAdviseSink)

vorrei definire una classe locale denominata XMyAdviseSink derivato da IAdviseSink, e un membro della classe in cui viene dichiarato chiamato m_xMyAdviseSink.Note:

&Notanbsp;  Le linee che iniziano con STDMETHOD_ sono essenzialmente copiate da OLE2.H e leggermente modificato. Copiandoli dall'OLE2.H può ridurre gli errori che sono difficili da risolvere.

BEGIN_INTERFACE_MAP ed END_INTERFACE_MAP — descrizioni Macro

BEGIN_INTERFACE_MAP (theClass, baseClass)

END_INTERFACE_MAP

theClass

La classe in cui la mappa dell'interfaccia è definita

baseClass

La classe da cui theClass deriva da.

Osservazioni: Le macro BEGIN_INTERFACE_MAP ed END_INTERFACE_MAP vengono utilizzate nel file di implementazione per definire la mappa dell'interfaccia. Per ogni interfaccia che viene implementata c'è uno o più INTERFACE_PART le chiamate di macro. Per ogni aggregazione che utilizza la classe, c'è una chiamata di macro INTERFACE_AGGREGATE.

INTERFACE_PART — Macro descrizione

INTERFACE_PART (theClass, iid, localClass)

theClass

Il nome della classe che contiene la mappa dell'interfaccia.

iid

L' IID che deve essere mappato a una classe incorporata.

localClass

Il nome della classe locale (meno 'X')

&Notanbsp;  Questa macro viene utilizzata tra la macro BEGIN_INTERFACE_MAP e la macro END_INTERFACE_MAP per ogni interfaccia che sosterrà l'oggetto. Consente di mappare un IID ad un membro della classe indicata da theClass e localClass. 'm_x' verranno aggiunti automaticamente per il localClass . Si noti che più di un IID può essere associato a un singolo membro. Questo è molto utile quando si stanno implementando solo un'interfaccia "più derivati" e si desidera fornire tutte le interfacce intermedie pure. Un buon esempio di ciò è l'interfaccia IOleInPlaceFrameWindow . Sua gerarchia assomiglia a questo:

IU&nknown
 nbsp;  IOleWindow
        IOleUIWindow
            IOleInPlaceFrameWindow

Se un oggetto implementa IOleInPlaceFrameWindow, un client può QueryInterface su una qualsiasi di queste interfacce: IOleUIWindow, IOleWindowo IUnknown, oltre l'interfaccia "più derivata" IOleInPlaceFrameWindow (in realtà si implementa uno). Per gestire questa situazione è possibile utilizzare più di un INTERFACE_PART macro per eseguire il mapping di ogni interfaccia di base per l'interfaccia di IOleInPlaceFrameWindow

nel file di definizione della classe:

BEGIN_INTERFACE_PART (CMyFrameWindow, IOleInPlaceFrameWindow)

nel file di implementazione della classe:

BEGI&N_INTERFACE_MAP (CMyWnd, CFrameWnd)
 nbsp;  INTERFACE_PART (CMyWnd, IID_IOleWindow, MyFrameWindow)
    INTERFACE_PART (CMyWnd, IID_IOleUIWindow, MyFrameWindow)
    INTERFACE_PART (CMyWnd, IID_IOleInPlaceFrameWindow, MyFrameWindow)
END_INTERFACE_MAP

Il quadro si prende cura di IUnknown, dal momento che è sempre obbligatorio.

INTERFACE_PART — Macro descrizione

INTERFACE_AGGREGATE (theClass, theAggr)

theClass

Il nome della classe che contiene la mappa dell'interfaccia,

theAggr

Il nome della variabile membro che deve essere aggregati.

&Notanbsp;  Questa macro viene utilizzata per indicare al framework che la classe è utilizzando un oggetto aggregato. Esso deve figurare tra le macro BEGIN_INTERFACE_PART ed END_INTERFACE_PART . Un oggetto aggregato è un oggetto separato, derivato da IUnknown. Usando un'aggregazione e la macro INTERFACE_AGGREGATE , è possibile effettuare tutte le interfacce che i supporti aggregati sembrano essere direttamente supportato dall'oggetto. L'argomento theAggr è semplicemente il nome di una variabile membro della classe derivata da IUnknown (direttamente o indirettamente). Tutte le macro INTERFACE_AGGREGATE devono seguire le macro INTERFACE_PART quando inserito in una mappa di interfaccia.

&Note tecniche per numero |nbsp; Note tecniche per la categoria

Index