Sdílet prostřednictvím


TN038: MFC/OLE – implementace třídy IUnknown

Poznámka

Následující technická poznámka se od prvního zahrnutí do online dokumentace neaktualizovala. V důsledku toho můžou být některé postupy a témata zastaralé nebo nesprávné. Nejnovější informace doporučujeme vyhledat v online indexu dokumentace, které vás zajímá.

Jádrem OLE 2 je "Objektový model komponenty OLE" nebo COM. Com definuje normu pro to, jak spolupracující objekty vzájemně komunikují. To zahrnuje podrobnosti o tom, jak "objekt" vypadá, včetně způsobu odeslání metod na objektu. COM také definuje základní třídu, ze které jsou odvozeny všechny třídy kompatibilní s modelu COM. Tato základní třída je IUnknown. IUnknown rozhraní se sice označuje jako třída C++, ale com není specifické pro žádný jazyk – lze jej implementovat v jazyce C, PASCAL nebo jiném jazyce, který podporuje binární rozložení objektu COM.

OLE označuje všechny třídy odvozené z IUnknown jako "rozhraní". Jedná se o důležitý rozdíl, protože "rozhraní", jako je IUnknown , nese bez implementace. Jednoduše definuje protokol, pomocí kterého objekty komunikují, nikoli specifika toho, co tyto implementace dělají. To je rozumné pro systém, který umožňuje maximální flexibilitu. Je to úloha MFC, která implementuje výchozí chování programů MFC/C++.

Abyste porozuměli implementaci knihovny MFC IUnknown , musíte nejprve pochopit, co toto rozhraní je. Zjednodušená verze IUnknown je definována níže:

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

Poznámka

Některé nezbytné podrobnosti o konvenci volání, například __stdcall jsou u tohoto obrázku vynecháné.

Členské funkce AddRef a Release řídí správu paměti objektu. Com používá schéma počítání odkazů ke sledování objektů. Na objekt se nikdy neodkazuje přímo stejně jako v jazyce C++. Místo toho jsou objekty MODELU COM vždy odkazovány ukazatelem. Pokud chcete uvolnit objekt při použití vlastníka, volá se člen verze objektu (na rozdíl od použití operátoru delete, stejně jako u tradičního objektu C++). Mechanismus počítání odkazů umožňuje správu více odkazů na jeden objekt. Implementace AddRef a Release udržuje počet odkazů na objekt – objekt se neodstraní, dokud jeho počet odkazů nedosáhne nuly.

AddRef a Release jsou poměrně jednoduché z hlediska implementace. Tady je triviální implementace:

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

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

Členová funkce QueryInterface je o něco zajímavější. Není velmi zajímavé mít objekt, jehož pouze členské funkce jsou AddRef a Release – bylo by hezké říct objektu, aby udělal více věcí, než IUnknown poskytuje. Tady je funkce QueryInterface užitečná. Umožňuje získat jiné "rozhraní" na stejném objektu. Tato rozhraní jsou obvykle odvozena od IUnknown a přidávají další funkce přidáním nových členských funkcí. Rozhraní modelu COM nikdy nemají členské proměnné deklarované v rozhraní a všechny členské funkce jsou deklarovány jako čistě virtuální. Příklad:

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

Pokud chcete získat IPrintInterface, pokud máte pouze IUnknown, zavolejte QueryInterface pomocí IID .IPrintInterface Je IID 128bitové číslo, které jednoznačně identifikuje rozhraní. Pro každé rozhraní, které definujete buď vy, nebo OLE, je k IID dispozici. Pokud je pUnk ukazatelem na objekt IUnknown , kód pro načtení IPrintInterface z něj může být:

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

To se zdá poměrně snadné, ale jak byste implementovali objekt podporující IPrintInterface i IUnknown rozhraní V tomto případě je to jednoduché, protože IPrintInterface je odvozen přímo z IUnknown – implementací IPrintInterface, IUnknown je automaticky podporován. Příklad:

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

Implementace AddRef a Release by byly naprosto stejné jako implementace uvedené výše. CPrintObj::QueryInterface vypadalo by nějak takto:

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

Jak vidíte, pokud je rozpoznán identifikátor rozhraní (IID), vrátí se ukazatel na váš objekt; v opačném případě dojde k chybě. Všimněte si také, že výsledkem úspěšného queryInterface je implicitní addRef. Samozřejmě byste také museli implementovat CEditObj::P rint. To je jednoduché, protože IPrintInterface byl přímo odvozen z IUnknown rozhraní. Pokud však chcete podporovat dvě různá rozhraní, obě odvozené z IUnknown, zvažte následující:

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

Ačkoli existuje mnoho různých způsobů implementace třídy podporující IEditInterface i IPrintInterface, včetně použití více dědičnosti jazyka C++, tato poznámka se soustředí na použití vnořených tříd k implementaci této funkce.

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;
};

Celá implementace je zahrnuta níže:

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);
}

Všimněte si, že většina implementace IUnknown je umístěna do CEditPrintObj třídy namísto duplikování kódu v CEditPrintObj::CEditObj a CEditPrintObj::CPrintObj. Tím se sníží množství kódu a vyhnete se chybám. Klíčovým bodem zde je to, že z rozhraní IUnknown je možné volat QueryInterface pro načtení libovolného rozhraní, které objekt může podporovat, a z každého z těchto rozhraní je možné provést totéž. To znamená, že všechny funkce QueryInterface dostupné z každého rozhraní se musí chovat úplně stejně. Aby tyto vložené objekty mohly volat implementaci ve "vnějším objektu", použije se zpětný ukazatel (m_pParent). Ukazatel m_pParent se inicializuje během konstruktoru CEditPrintObj. Pak byste implementovali CEditPrintObj::CPrintObj::P rintObject a CEditPrintObj::CEditObj::EditObject. K přidání jedné funkce byla přidána docela část kódu – možnost upravit objekt. Naštěstí je poměrně neobvyklé, že rozhraní mají pouze jednu členovou funkci (i když k tomu dochází) a v tomto případě by se EditObject a PrintObject obvykle zkombinovaly do jednoho rozhraní.

To je hodně vysvětlení a hodně kódu pro takový jednoduchý scénář. Třídy MFC/OLE poskytují jednodušší alternativu. Implementace MFC používá metodu podobnou způsobu, jakým jsou zprávy systému Windows zabaleny pomocí message Mapy. Toto zařízení se nazývá rozhraní Mapy a je popsáno v další části.

MAPY rozhraní MFC

MFC/OLE zahrnuje implementaci rozhraní Mapy, podobně jako Mapy zpráv mfc a dispatch Mapy v konceptu a provádění. Základní funkce rozhraní MFC Mapy jsou následující:

Mapy rozhraní navíc podporují následující pokročilé funkce:

  • Podpora vytváření agregatable objektů MODELU COM

  • Podpora použití agregačních objektů při implementaci objektu COM

  • Implementace je připojitelná a rozšiřitelná.

Další informace o agregaci najdete v tématu Agregace .

Podpora mapování rozhraní MFC je kořenem třídy CCmdTarget . CCmdTarget Počet odkazů "has-a" a také všechny členské funkce přidružené k implementaci IUnknown (například počet odkazů je v CCmdTarget). Chcete-li vytvořit třídu, která podporuje OLE COM, odvozujete třídu z CCmdTarget různých maker a také členské funkce CCmdTarget pro implementaci požadovaných rozhraní. Implementace MFC používá vnořené třídy k definování každé implementace rozhraní podobně jako v příkladu výše. To je jednodušší díky standardní implementaci IUnknown i řady maker, která eliminují některé opakující se kódy.

Základy mapování rozhraní

Implementace třídy pomocí map rozhraní MFC

  1. Odvození třídy buď přímo nebo nepřímo z CCmdTarget.

  2. DECLARE_INTERFACE_MAP Použijte funkci v definici odvozené třídy.

  3. Pro každé rozhraní, které chcete podporovat, použijte BEGIN_INTERFACE_PART a END_INTERFACE_PART makra v definici třídy.

  4. V souboru implementace použijte BEGIN_INTERFACE_MAP a END_INTERFACE_MAP makra k definování mapy rozhraní třídy.

  5. Pro každou podporovanou technologii IID použijte makro INTERFACE_PART mezi BEGIN_INTERFACE_MAP a END_INTERFACE_MAP makry k namapování iiD na určitou "část" vaší třídy.

  6. Implementujte všechny vnořené třídy, které představují rozhraní, která podporujete.

  7. Pomocí METHOD_PROLOGUE makra se dostanete k nadřazeného CCmdTargetobjektu -odvozeného objektu.

  8. AddRef, Release a QueryInterface mohou delegovat na CCmdTarget implementaci těchto funkcí (ExternalAddRef, ExternalReleasea ExternalQueryInterface).

Výše uvedený příklad CPrintEditObj lze implementovat takto:

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)
};

Výše uvedená deklarace vytvoří třídu odvozenou z CCmdTarget. Makro DECLARE_INTERFACE_MAP říká rozhraní, že tato třída bude mít vlastní mapování rozhraní. Kromě toho BEGIN_INTERFACE_PART a END_INTERFACE_PART makra definují vnořené třídy, v tomto případě s názvy CEditObj a CPrintObj (X se používá pouze k rozlišení vnořených tříd od globálních tříd, které začínají na "C" a třídy rozhraní, které začínají na "I"). Vytvoří se dva vnořené členy těchto tříd: m_CEditObj a m_CPrintObj. Makra automaticky deklarují funkce AddRef, Release a QueryInterface. Proto deklarujete pouze funkce specifické pro toto rozhraní: EditObject a PrintObject (makro OLE STDMETHOD se používá tak, aby se _stdcall a virtuální klíčová slova poskytovala podle potřeby pro cílovou platformu).

Implementace mapování rozhraní pro tuto třídu:

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

Tím se IID_IPrintInterface IID spojí s m_CPrintObj a IID_IEditInterface s m_CEditObj. Implementace CCmdTarget QueryInterface (CCmdTarget::ExternalQueryInterface) používá tuto mapu k vrácení ukazatelů na m_CPrintObj a m_CEditObj při vyžádání. Není nutné zahrnout položku pro IID_IUnknown; architektura použije první rozhraní v mapě (v tomto případě m_CPrintObj) při IID_IUnknown vyžádání.

I když makro BEGIN_INTERFACE_PART automaticky deklaroval funkce AddRef, Release a QueryInterface za vás, musíte je stále implementovat:

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

Implementace pro CEditPrintObj::CPrintObj, by byla podobná výše uvedené definice pro CEditPrintObj::CEditObj. I když by bylo možné vytvořit makro, které by bylo možné použít k automatickému vygenerování těchto funkcí (ale dříve ve vývoji MFC/OLE to byl tento případ), je obtížné nastavit zarážky, když makro generuje více než jeden řádek kódu. Z tohoto důvodu se tento kód rozbalí ručně.

Pomocí implementace architektury map zpráv existuje celá řada věcí, které nebyly nutné provést:

  • Implementace queryInterface

  • Implementace metody AddRef a release

  • Deklarujte některou z těchto předdefinovaných metod v obou rozhraních.

Kromě toho architektura používá mapy zpráv interně. To vám umožní odvodit z třídy architektury, řekněme COleServerDoc, že již podporuje určitá rozhraní a poskytuje buď náhrady nebo doplňky rozhraní poskytované architekturou. Můžete to provést, protože architektura plně podporuje dědění mapy rozhraní ze základní třídy. To je důvod, proč BEGIN_INTERFACE_MAP přebírá jako druhý parametr název základní třídy.

Poznámka

Obecně není možné opakovaně používat implementaci předdefinovaných implementací rozhraní OLE mfc pouze tak, že dědí vloženou specializaci tohoto rozhraní z verze MFC. To není možné, protože použití METHOD_PROLOGUE makra k získání přístupu k obsahující CCmdTarget-odvozený objekt znamená pevný posun vloženého objektu z -odvozeného objektu CCmdTarget. To znamená, že například nelze odvodit vložený XMyAdviseSink z implementace MFC v COleClientItem::XAdviseSink, protože XAdviseSink spoléhá na konkrétní posun od horní části objektu COleClientItem .

Poznámka

Můžete však delegovat na implementaci MFC pro všechny funkce, které chcete použít výchozí chování MFC. To se provádí v implementaci IOleInPlaceFrame MFC (XOleInPlaceFrame) ve COleFrameHook třídě (deleguje na m_xOleInPlaceUIWindow pro mnoho funkcí). Tento návrh byl zvolen ke zmenšení velikosti modulu runtime objektů, které implementují mnoho rozhraní; eliminuje potřebu zpětného ukazatele (například způsob použití m_pParent v předchozí části).

Mapy agregace a rozhraní

Kromě podpory samostatných objektů MODELU COM podporuje mfc také agregaci. Agregace sama o sobě je příliš složitá téma, které je zde popsáno; Další informace o agregaci najdete v tématu Agregace . Tato poznámka jednoduše popisuje podporu agregace integrované do rozhraní a map rozhraní.

Agregaci lze použít dvěma způsoby: (1) pomocí objektu COM, který podporuje agregaci, a (2) implementaci objektu, který lze agregovat jiným objektem. Tyto funkce se dají označovat jako "použití agregovaného objektu" a "vytvoření agregace objektu". MFC podporuje obojí.

Použití agregovaného objektu

Pokud chcete použít agregační objekt, musí existovat nějaký způsob, jak svázat agregaci do mechanismu QueryInterface. Jinými slovy, agregační objekt se musí chovat, jako by byl nativní součástí objektu. Jak to tedy souvisí s mechanismem mapování rozhraní MFC Kromě INTERFACE_PART makra, kde je vnořený objekt mapován na IID, můžete také deklarovat agregační objekt jako součást odvozené CCmdTarget třídy. K tomu slouží INTERFACE_AGGREGATE makro. To vám umožní určit členovou proměnnou (což musí být ukazatel na IUnknown nebo odvozenou třídu), která se má integrovat do mechanismu mapování rozhraní. Pokud ukazatel není null při CCmdTarget::ExternalQueryInterface volání, rozhraní automaticky zavolá agregační objekt QueryInterface členské funkce, pokud IID požadovaná není jedním z nativních IIDobjektů podporovaných samotným objektem CCmdTarget .

Použití makra INTERFACE_AGGREGATE

  1. Deklarujte členovou proměnnou (an IUnknown*), která bude obsahovat ukazatel na agregovaný objekt.

  2. Do mapy rozhraní zahrňte INTERFACE_AGGREGATE makro, které odkazuje na členovou proměnnou podle názvu.

  3. V určitém okamžiku (obvykle během CCmdTarget::OnCreateAggregates) inicializovat členovou proměnnou na něco jiného než NULL.

Příklad:

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()

Proměnná m_lpAggrInner je inicializována v konstruktoru na hodnotu NULL. Architektura ignoruje proměnnou člena NULL ve výchozí implementaci QueryInterface. OnCreateAggregates je vhodné místo pro skutečné vytvoření agregačních objektů. Pokud vytváříte objekt mimo implementaci MFC, COleObjectFactorybudete ho muset explicitně volat. Při vytváření agregačních objektů a použití CCmdTarget::GetControllingUnknown se při vytváření agregovatelných objektů projeví důvod vytváření agregačních CCmdTarget::OnCreateAggregates objektů.

Tato technika poskytne objektu všechna rozhraní, která agregační objekt podporuje a jeho nativní rozhraní. Pokud chcete pouze podmnožinu rozhraní, která agregace podporuje, můžete přepsat CCmdTarget::GetInterfaceHook. To umožňuje velmi nízkou úroveň připojení, podobně jako QueryInterface. Obvykle chcete všechna rozhraní, která agregace podporuje.

Vytvoření agregatable implementace objektu

Aby byl objekt agregatable, musí implementace AddRef, Release a QueryInterface delegovat na "řízení neznámé". Jinými slovy, aby byl součástí objektu, musí delegovat AddRef, Release a QueryInterface na jiný objekt, také odvozený z IUnknown. Toto "řízení neznámé" je poskytováno objektu při jeho vytvoření, to znamená, že je poskytována k implementaci COleObjectFactory. Implementace této operace má malou režii a v některých případech není žádoucí, proto mfc tuto možnost zpřístupňuje jako volitelnou. Chcete-li povolit agregatable objektu, voláte CCmdTarget::EnableAggregation z konstruktoru objektu.

Pokud objekt také používá agregace, musíte také předat správné "řízení neznámé" agregačním objektům. Obvykle se tento ukazatel IUnknown předá objektu při vytvoření agregace. Například pUnkOuter parametr je "řízení neznámý" pro objekty vytvořené pomocí CoCreateInstance. Správné "řízení neznámého" ukazatele lze načíst voláním CCmdTarget::GetControllingUnknown. Hodnota vrácená z této funkce však není během konstruktoru platná. Z tohoto důvodu se doporučuje vytvořit agregace pouze v přepsání CCmdTarget::OnCreateAggregates, kde návratová hodnota z GetControllingUnknown je spolehlivá, i když je vytvořena z COleObjectFactory implementace.

Je také důležité, aby objekt při přidávání nebo uvolňování počtu umělých odkazů manipuloval se správným počtem odkazů. Aby se zajistilo, že se jedná o tento případ, vždy volejte ExternalAddRef místo ExternalRelease InternalRelease a InternalAddRef. Volání nebo InternalAddRef volání InternalRelease třídy, která podporuje agregaci, je vzácné.

Referenční materiál

Pokročilé použití OLE, například definování vlastních rozhraní nebo přepsání implementace rozhraní OLE vyžaduje použití základního mechanismu mapování rozhraní.

Tato část popisuje jednotlivá makra a rozhraní API, která se používají k implementaci těchto pokročilých funkcí.

CCmdTarget::EnableAggregation – popis funkce

void EnableAggregation();

Poznámky

Volání této funkce v konstruktoru odvozené třídy, pokud chcete podporovat ole agregace pro objekty tohoto typu. Tím se připraví speciální implementace IUnknown, která se vyžaduje pro agregatable objekty.

CCmdTarget::ExternalQueryInterface – popis funkce

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

Parametry

lpIID
Daleko ukazatel na IID (první argument queryInterface)

ppvObj
Ukazatel na IUnknown* (druhý argument dotazuInterface)

Poznámky

Volání této funkce v implementaci IUnknown pro každé rozhraní, které vaše třída implementuje. Tato funkce poskytuje standardní implementaci QueryInterface řízenou daty na základě mapy rozhraní objektu. Návratovou hodnotu je nutné přetypovat na hodnotu HRESULT. Pokud je objekt agregovaný, bude tato funkce místo použití mapy místního rozhraní volat "řízení IUnknown".

CCmdTarget::ExternalAddRef – popis funkce

DWORD ExternalAddRef();

Poznámky

Volání této funkce ve vaší implementaci IUnknown::AddRef pro každé rozhraní, které vaše třída implementuje. Návratová hodnota je nový referenční počet na CCmdTarget objektu. Pokud je objekt agregovaný, bude tato funkce místo manipulace s místním počtem odkazů volat "řízení IUnknown".

CCmdTarget::ExternalRelease – popis funkce

DWORD ExternalRelease();

Poznámky

Volání této funkce v implementaci IUnknown::Release pro každé rozhraní, které vaše třída implementuje. Vrácená hodnota označuje nový počet odkazů na objekt. Pokud je objekt agregovaný, bude tato funkce místo manipulace s místním počtem odkazů volat "řízení IUnknown".

DECLARE_INTERFACE_MAP – popis makra

DECLARE_INTERFACE_MAP

Poznámky

Toto makro použijte ve všech třídách odvozených z CCmdTarget toho, které budou mít mapování rozhraní. Používá se stejným způsobem jako DECLARE_MESSAGE_MAP. Toto vyvolání makra by mělo být umístěné v definici třídy, obvykle v záhlaví (. H) soubor. Třída s DECLARE_INTERFACE_MAP musí definovat mapování rozhraní v souboru implementace (. CPP) s makry BEGIN_INTERFACE_MAP a END_INTERFACE_MAP.

BEGIN_INTERFACE_PART a END_INTERFACE_PART – popisy maker

BEGIN_INTERFACE_PART(localClass, iface);
END_INTERFACE_PART(localClass)

Parametry

localClass
Název třídy, která implementuje rozhraní

iface
Název rozhraní, které tato třída implementuje

Poznámky

Pro každé rozhraní, které vaše třída implementuje, musíte mít BEGIN_INTERFACE_PART a END_INTERFACE_PART dvojici. Tato makra definují místní třídu odvozenou z rozhraní OLE, které definujete, a také vloženou členovou proměnnou této třídy. Členy AddRef, Release a QueryInterface se deklarují automaticky. Musíte zahrnout deklarace pro ostatní členské funkce, které jsou součástí implementovaného rozhraní (tyto deklarace jsou umístěny mezi BEGIN_INTERFACE_PART a END_INTERFACE_PART makry).

Argument iface je rozhraní OLE, které chcete implementovat, například IAdviseSink, nebo IPersistStorage (nebo vlastní rozhraní).

Argument localClass je název místní třídy, která bude definována. Před názvem se automaticky vytvoří symbol X. Tato konvence vytváření názvů se používá k zabránění kolizím s globálními třídami stejného názvu. Kromě toho je název vloženého členu stejný jako název localClass s tím rozdílem, že má předponu "m_x".

Příklad:

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)

by definovala místní třídu s názvem XMyAdviseSink odvozenou z IAdviseSink a člen třídy, ve které je deklarován m_xMyAdviseSink.Poznámka:

Poznámka

Řádky začínající STDMETHOD_ jsou v podstatě zkopírovány z OLE2. H a mírně upraveno. Zkopírujete je z OLE2. H může omezit chyby, které se obtížně řeší.

BEGIN_INTERFACE_MAP a END_INTERFACE_MAP – popisy maker

BEGIN_INTERFACE_MAP(theClass, baseClass)
END_INTERFACE_MAP

Parametry

theClass
Třída, ve které má být definována mapa rozhraní

Baseclass
Třída, ze které Třída je odvozena.

Poznámky

Makra BEGIN_INTERFACE_MAP a END_INTERFACE_MAP se ve implementačním souboru používají k definování mapy rozhraní. Pro každé rozhraní, které je implementováno, existuje jeden nebo více INTERFACE_PART vyvolání maker. Pro každou agregaci, kterou třída používá, existuje jedna INTERFACE_AGGREGATE vyvolání makra.

INTERFACE_PART – popis makra

INTERFACE_PART(theClass, iid, localClass)

Parametry

theClass
Název třídy, která obsahuje mapu rozhraní.

Iid
To IID je namapováno na vloženou třídu.

localClass
Název místní třídy (méně X).

Poznámky

Toto makro se používá mezi BEGIN_INTERFACE_MAP makrem a END_INTERFACE_MAP makrem pro každé rozhraní, které objekt bude podporovat. Umožňuje namapovat IID na člen třídy označenou třídouClass a localClass. Do místní třídy se automaticky přidá m_x. Všimněte si, že k jednomu členu může být přidruženo více než jeden IID člen. To je velmi užitečné, když implementujete pouze "nejvíce odvozené" rozhraní a chcete poskytnout také všechna zprostředkující rozhraní. Dobrým příkladem je IOleInPlaceFrameWindow rozhraní. Její hierarchie vypadá takto:

IUnknown
    IOleWindow
        IOleUIWindow
            IOleInPlaceFrameWindow

Pokud objekt implementuje IOleInPlaceFrameWindow, klient může QueryInterface na některé z těchto rozhraní: IOleUIWindow, IOleWindow, nebo IUnknown, kromě "nejvíce odvozeného" rozhraní IOleInPlaceFrameWindow (ten, který skutečně implementujete). K tomu můžete použít více než jedno INTERFACE_PART makro k mapování každého a každého základního IOleInPlaceFrameWindow rozhraní na rozhraní:

v definičním souboru třídy:

BEGIN_INTERFACE_PART(CMyFrameWindow, IOleInPlaceFrameWindow)

v souboru implementace třídy:

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

Architektura se stará o IUnknown, protože je vždy nutná.

INTERFACE_PART – popis makra

INTERFACE_AGGREGATE(theClass, theAggr)

Parametry

theClass
Název třídy, která obsahuje mapu rozhraní,

theAggr
Název členské proměnné, která se má agregovat.

Poznámky

Toto makro slouží k určení architektury, že třída používá agregační objekt. Musí se zobrazit mezi makry BEGIN_INTERFACE_PART a END_INTERFACE_PART. Agregační objekt je samostatný objekt odvozený od IUnknown. Pomocí agregace a INTERFACE_AGGREGATE makra můžete nastavit, aby všechna rozhraní, která agregace podporuje, byla objektem přímo podporována. Argument TheAggr je jednoduše název členské proměnné vaší třídy, která je odvozena z IUnknown (buď přímo nebo nepřímo). Všechna INTERFACE_AGGREGATE makra musí při umístění do mapy rozhraní dodržovat INTERFACE_PART makra.

Viz také

Technické poznámky podle čísel
Technické poznámky podle kategorií