TN038: MFC/OLE – implementace třídy IUnknown
[!POZNÁMKA]
Následující technická poznámka nebyla aktualizována, protože byla poprvé zahrnuta v dokumentaci online.V důsledku toho některé postupy a témata mohou být nesprávné nebo zastaralé.Pro nejnovější informace je vhodné vyhledat téma zájmu v dokumentaci online index.
V srdci objektu OLE 2 je „OLE Component Object Model“ nebo COM.COM definuje standard, jak mezi sebou komunikují spolupracující objekty.Patří sem podrobné informace o tom, jak "objekt" vypadá, včetně toho, jaké metody se odesílají v objektu.COM také definuje základní třídu, ze které jsou odvozeny všechny třídy kompatibilní s COM.Tato základní třída je IUnknown.I když je rozhraní IUnknown odkazováno jako třída C++, COM není specifický pro žádný jazyk – lze je implementovat do jazyka C, PASCAL nebo jakéhokoli jiného jazyka, který podporuje binární rozložení objektu COM.
OLE se vztahuje na všechny třídy odvozené z IUnknown jako "rozhraní". To je důležité rozlišení, protože "rozhraní" jako IUnknown s ním neprovádí implementaci.Pouze definuje protokol, podle kterého objekty komunikují, ale nespecifikuje, co tyto implementace dělají.To je vhodné pro systém, který umožňuje maximální flexibilitu.Je úkolem úlohy MFC implementovat výchozí chování pro aplikace MFC/C++.
Pro pochopení implementace MFC atributu IUnknown musíte nejprve porozumět, jaké je toto rozhraní.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]
Podrobnosti o určité nezbytné konvenci volání, jako __stdcall jsou pro tento obrázek vynechány.
Členské funkce AddRef a Verze řídí správu paměti objektu.COM používá schéma součtu odkazů ke sledování objektů.Nikdy není nikdy odkazován přímo jako v jazyce C++.Místo toho jsou objekty COM vždy odkazovány prostřednictvím ukazatele.K uvolnění objektu poté, co ho vlastník přestal používat, bude volán prvek Release objektu (na rozdíl od použití operátoru delete, jako by se dělalo pro tradiční objekt jazyka C++).Mechanismus pro počítání referencí umožňuje spravovat více odkazů na jeden objekt.Implementace AddRef a Release udržuje počet odkazů na objekt – objekt není odstraněn, dokud jeho počet odkazů nedosáhne nuly.
AddRef a Release jsou z hlediska implementace poměrně jednoduché.Zde je triviální implementace:
ULONG CMyObj::AddRef()
{
return ++m_dwRef;
}
ULONG CMyObj::Release()
{
if (--m_dwRef == 0)
{
delete this;
return 0;
}
return m_dwRef;
}
Člen funkce QueryInterface je o něco zajímavější.Není velmi výhodné disponovat objektem, jehož jedinými členskými funkcemi je AddRef a Release – by bylo výhodné udat objektu více příkazů, než kterými disponuje IUnknown.V tomto místě je užitečná funkce QueryInterface.Umožňuje získat různá "rozhraní" ve stejném objektu.Tato rozhraní jsou obvykle odvozena z IUnknown a přidávají další funkce přidáním nových funkcí člena.Rozhraní COM nikdy nemají členské proměnné deklarované v rozhraní a funkce všech členů 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, volejte QueryInterface pomocí IID z IPrintInterface.IID je 128bitové číslo, které rozhraní jedinečně identifikuje.Existuje IID pro každé rozhraní, které OLE definuje.Pokud pUnk je ukazatelem na objekt IUnknown, kód k 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
}
Zdá se to být poměrně snadné, ale jak můžete implementovat objekt podporující rozhraní IPrintInterface i IUnknown?V tomto případě je to jednoduché vzhledem k tomu, že IPrintInterface je odvozen přímo z IUnknown – implementací IPrintInterface je 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 byla přesně shodná s prvky implementovanými výše.CPrintObj::QueryInterface by vypadalo přibližně 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 můžete vidět, pokud je identifikátor rozhraní (IID) rozpoznán, je vrácen ukazatel na váš objekt; v opačném případě dojde k chybě.Všimněte si také, že úspěšné QueryInterface vede k předpokládanému AddRef.Samozřejmě je také třeba implementovat CEditObj::Print.To je jednoduché, protože IPrintInterface byla odvozena přímo z rozhraní IUnknown.Nicméně, pokud jste chtěli podporu dvou různých rozhraní, obě odvozeny od IUnknown, zvažte následující:
class IEditInterface : public IUnkown
{
public:
virtual void EditObject() = 0;
};
Přestože existuje několik způsobů, jak implementovat třídu podporující rozhraní IEditInterface a IPrintInterface, včetně použití vícenásobné dědičnosti C++, tato poznámka se soustředí na používání vnořených tříd pro 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é provedení je zahrnuto 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 implementací IUnknown je umístěna do třídy CEditPrintObj namísto duplikování kódu v CEditPrintObj::CEditObj a CEditPrintObj::CPrintObj.To snižuje množství kódu a zabraňuje chybám.Důležitým faktem zde je, že je možné volat z rozhraní IUnknown QueryInterface a načíst tak libovolné rozhraní, které může objekt podporovat a z každého z těchto rozhraní je možné provádět to stejné.To znamená, že všechny funkce rozhraní QueryInterface dostupné z každého rozhraní se musí chovat přesně stejným způsobem.Aby tyto vložené objekty volaly implementaci ve "vnějším objektu" použije se zpětný ukazatel (m_pParent).Během konstruktoru CEditPrintObj je inicializován ukazatel m_pParent.Pak byste implementovali také CEditPrintObj::CPrintObj::PrintObject a CEditPrintObj::CEditObj::EditObject.Byl přidán poměrně velký kus kódu a tím i nová funkce – možnost upravit objekt.Naštěstí je poměrně neobvyklé pro rozhraní, aby měly pouze jednu členskou funkci (i když k tomu dochází) a v takovém případě by EditObject a PrintObject obvykle byly sloučeny do jediného rozhraní.
To je velké množství vysvětlení a velké množství kódu pro tak jednoduchý scénář.Třídy MFC/OLE poskytují jednodušší alternativu.Implementace MFC používá techniku podobnou způsobu, jakým jsou zprávy systému Windows zabaleny s mapami zpráv.Toto zařízení se nazývá Mapy rozhraní a je popsáno v následující části.
Mapy rozhraní MFC
MFC/OLE obsahuje implementaci "Mapy rozhraní" v pojetí a provedení podobně, jako "Mapy zprávy" a "Mapy odeslání" v MFC.Základní funkce mapování rozhraní knihovny MFC jsou následující:
Standardní implementace rozhraní IUnknown, vestavěná do třídy CCmdTarget.
Implementace dat řízených pomocí QueryInterface
Kromě toho mapy rozhraní podporují následující rozšířené funkce:
Podpora pro vytváření agregovatelných objektů COM
Podpora pro používání agregovaných objektů v implementaci objektu COM.
Implementace je připojitelná a rozšiřitelná
Další informace o agregaci naleznete v tématu Agregace.
Podpora mapování rozhraní v MFC je zakotvena ve třídě CCmdTarget.CCmdTarget referenční počet„has-a“ stejně jako všechny členské funkce přidružené k implementaci IUnknown (počet odkazů je například CCmdTarget).Chcete-li vytvořit třídu, která podporuje OLE COM, odvoďte třídu z CCmdTarget a použijte různá makra a členské funkce CCmdTarget k implementaci požadovaných rozhraní.Implementace MFC používá vnořené třídy pro definici jednotlivých implementací rozhraní, podobně jako v příkladu výše.To se usnadní pomocí standardní implementace IUnknown a počtu maker, které eliminují některý opakovaný kód.
Chcete-li implementovat třídu pomocí map rozhraní MFC:
Přímo nebo nepřímo odvodit třídu z CCmdTarget.
Použijte funkci DECLARE_INTERFACE_MAP v definici odvozené třídy.
U každého rozhraní, které chcete podporovat, použijte makra BEGIN_INTERFACE_PART a END_INTERFACE_PART v definici třídy.
V souboru implementace pomocí makra BEGIN_INTERFACE_MAP a END_INTERFACE_MAP definujte mapování rozhraní třídy.
Každý podporovaný identifikátor IID použije makro INTERFACE_PART mezi makry BEGIN_INTERFACE_MAP a END_INTERFACE_MAP pro mapování daného IID na konkrétní "část" vaší třídy.
Implementace všech vnořených tříd, které představují rozhraní, která podporujete.
Pomocí makra METHOD_PROLOGUE přistupujte k nadřízenému odvozenému objektu CCmdTarget.
AddRef, Release a QueryInterface může delegovat na CCmdTarget implementaci těchto funkcí (ExternalAddRef, ExternalRelease a ExternalQueryInterface).
Výše uvedený příklad CPrintEditObj je implementován 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é prohlášení vytvoří třídu odvozenou z CCmdTarget.Makro DECLARE_INTERFACE_MAP říká rozhraní framework, že tato třída bude mít vlastní mapu rozhraní.Kromě toho makra BEGIN_INTERFACE_PART a END_INTERFACE_PART definují vnořené třídy, v tomto případě s názvy CEditObj a CPrintObj (X slouží pouze k odlišení vnořených tříd od globálních tříd, které začínají na "C" a tříd rozhraní, které začínají na "I").Jsou vytvořeny dva vnoření členové těchto tříd: m_CEditObj a m_CPrintObj v uvedeném pořadí.Makra umožňují automaticky deklarovat funkce AddRef, Release a QueryInterface. Proto je pouze deklarovat pouze funkce specifické pro toto rozhraní: EditObject a PrintObject (makro OLE STDMETHOD se používá tak, aby _stdcall a virtuální klíčová slova byla podle potřeby k dispozici pro cílovou platformu).
Chcete-li implementovat mapu 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()
To spojí IID_IPrintInterface IID s m_CPrintObj a IID_IEditInterface s m_CEditObj v uvedeném pořadí.Implementace CCmdTargetQueryInterface (CCmdTarget::ExternalQueryInterface) používá tuto mapu k vrácení ukazatele do m_CPrintObj a m_CEditObj na požádání.Není nutné zahrnout položku pro IID_IUnknown; pokud je vyžadován IID_IUnknown, rámec použije první mapované rozhraní (v tomto případě m_CPrintObj).
I když makro BEGIN_INTERFACE_PART za vás automaticky deklarovalo funkce AddRef, Release a QueryInterface stále je musíte 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 CEditPrintObj::CPrintObj, bude shodná s výše uvedenou definicí pro CEditPrintObj::CEditObj.Ačkoli je možné vytvořit makro, které lze použít k automatickému generování těchto funkcí (ale dříve při vývoji technologie MFC/OLE se jednalo o tento případ), bude obtížné nastavit body, když makro vytvoří více než jeden řádek kódu.Z tohoto důvodu je tento kód rozbalen ručně.
Při implementaci rozhraní mapování zpráv existuje několik věcí, které nebylo nezbytné učinit:
Implementace QueryInterface
Implementace AddRef a Release
Deklarace některé z těchto vestavěných metod na obě vaše rozhraní
Kromě toho rozhraní používá interní mapy zpráv.Tímto způsobem lze odvodit z třídy systému, řekněme COleServerDoc, která již podporuje určitá rozhraní a poskytuje náhrady nebo dodatky k rozhraním poskytovaným tímto systémem.To můžete provést, protože systém plně podporuje dědění mapy rozhraní ze základní třídy.To je důvod proč BEGIN_INTERFACE_MAP bere jako svůj druhý parametr název základní třídy.
[!POZNÁMKA]
Obecně není možné znovu použít implementaci předdefinovaných implementací MFC rozhraní OLE pouhým zděděním vložené specializace rozhraní z verze MFC.To není možné protože použití makra METHOD_PROLOGUE k získání přístupu do obsahujícího objektu s odvozením CCmdTarget implikuje pevné odsazení vloženého objektu z objektu odvozeného z CCmdTarget.To například znamená, že nemůžete odvodit vestavěnou funkci XMyAdviseSink z implementace MFC v COleClientItem::XAdviseSink, protože XAdviseSink spoléhá na to, že je v konkrétním odsazení od horní části objektu COleClientItem.
[!POZNÁMKA]
Můžete však delegovat do implementace MFC pro všechny funkce, které mají mít výchozí chování knihovny MFC.To se provádí v implementaci MFC IOleInPlaceFrame (XOleInPlaceFrame) v třídě COleFrameHook (deleguje se do m_xOleInPlaceUIWindow pro mnoho funkcí).Tento návrh byl zvolen ke zmenšení velikosti runtime objektů, které implementují mnoho rozhraní; eliminuje potřebu ukazatel zpětného ukazatele (jako způsob m_pParent použitý v předchozím oddílu).
Agregace a mapy rozhraní
Kromě podpory samostatných objektů COM, MFC také podporuje agregaci.Agregace sama o sobě je pro rozebrání na místě příliš složité téma. Další informace o agregaci naleznete v tématu https://msdn.microsoft.com/library/windows/desktop/ms686558(v=vs.85).aspx.Tato poznámka bude jednoduše popisovat podporu agregace integrované v mapách systému a rozhraní.
Existují dva způsoby použití agregace: (1) použití objektu COM, který podporuje agregaci, a (2) implementace objektu, který může být agregovaný jiným.Na tyto možnosti lze odkazovat jako na „použití agregovaného objektu“ a „nastavení objektu jako agregovatelného“.MFC podporuje oboje.
Použití agregovaného objektu
Chcete-li použít agregovaný objekt, musí existovat způsob, jak navázat agregát na mechanismus QueryInterface.Jinými slovy agregovaný objekt se musí chovat, jako by se jednalo o nativní součást vašeho objektu.Jak to tedy pasuje do mechanismu mapování rozhraní MFC?Kromě makra INTERFACE_PART, je-li vnořený objekt mapován na IID, můžete také deklarovat agregovaný objekt jako součást vaší odvozené třídy CCmdTarget.K tomu je použito makro INTERFACE_AGGREGATE.To vám umožní určit členskou proměnnou (která musí být ukazatelem na třídu IUnknown nebo odvozenou třídu), které mají být začleněny do mechanismu mapování rozhraní.Pokud ukazatel není NULL při volání CCmdTarget::ExternalQueryInterface, rozhraní automaticky zavolá agregovanou členskou funkci QueryInterface objektu, pokud požadované IID nejsou nativní IID podporované samotným objektem CCmdTarget.
Použití makra INTERFACE_AGGREGATE
Deklarace členské proměnné (IUnknown*) která bude obsahovat ukazatel na agregovaný objekt.
Obsahuje makro INTERFACE_AGGREGATE v mapě rozhraní odkazující na členskou proměnnou podle názvu.
V určitém okamžiku (obvykle během CCmdTarget::OnCreateAggregates) se inicializuje členská proměnná na jinou hodnotu 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.Rámec ignoruje členskou proměnnou NULL ve výchozí implementaci QueryInterface.OnCreateAggregates je vhodné místo pro vytvoření skutečných agregovaných objektů.Musíte jej volat explicitně, pokud vytváříte objekt mimo implementaci MFC COleObjectFactory.Důvody vytvoření souhrnných ukazatelů v CCmdTarget::OnCreateAggregates a využití CCmdTarget::GetControllingUnknown se při diskuzi o vytváření agregovatelných objektů stanou zjevnými.
Tato technika vám poskytne vašemu objektu všechna rozhraní, která agregovaný objekt podporuje, a jeho nativní rozhraní.Pokud chcete pouze podmnožinu rozhraní, která podporuje agregace, můžete přepsat CCmdTarget::GetInterfaceHook.To umožňuje připojení na velmi nízké úrovni, podobné funkci QueryInterface.Obvykle chcete všechna rozhraní, která podporuje agregace.
Zpřístupnění agregace implementace objektu
Pro nastavení agregovatelnosti objektu musí být implementace AddRef, Release a QueryInterface delegovány na "řídící neznámou." Jinými slovy, aby mohl být součástí objektu, je třeba delegovat AddRef, Vydání a QueryInterface na jiný objekt, také odvozený z IUnknown.Tato "řídicí neznámá" je do objektu předána při jeho vytvoření, to znamená, že je poskytována pro implementaci COleObjectFactory.Implementace přináší malé množství režie a v některých případech není žádoucí, takže knihovny MFC toto provádějí volitelně.Pokud chcete umožnit možnost agregace objektu, volejte CCmdTarget::EnableAggregation z konstruktoru objektu.
Pokud objekt také používá agregace, musíte také zajistit správný průchod "řídící neznámé" pro agregaci objektů.Obvykle je tento ukazatel IUnknown je předán do objektu při vytvoření agregace.Například parametr pUnkOuter je "řídící neznámá" pro objekty vytvořené pomocí CoCreateInstance.Správný ukazatel "řídící neznámá" může být načten voláním CCmdTarget::GetControllingUnknown.Hodnota vrácená z této funkce však není během sestavování konstruktoru platná.Z tohoto důvodu doporučujeme vytvořit vaše agregace pouze v přepsání CCmdTarget::OnCreateAggregates, kde návratová hodnota ze GetControllingUnknown je spolehlivá, i když je vytvořena z implementace COleObjectFactory.
Je také důležité, že objekt manipuluje počtem správných odkazů při přidávání nebo uvolnění počtů umělých odkazů.Aby se zajistilo, že tomu tak bude, vždy volejte ExternalAddRef a ExternalRelease místo InternalRelease a InternalAddRef.Zřídka dochází k volání InternalRelease nebo InternalAddRef do třídy, která podporuje agregaci.
Referenční materiál
Pokročilé použití OLE, jako je definování vlastního 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á slouží k implementaci těchto rozšířených funkcí.
CCmdTarget::EnableAggregation – Popis funkce
void EnableAggregation();
Poznámky
Zavolejte tuto funkci v konstruktoru odvozené třídy, pokud si přejete podporovat agregace OLE pro objekty tohoto typu.To připraví speciální implementaci IUnknown, která je požadována pro agregovatelné objekty.
CCmdTarget::ExternalQueryInterface – Popis funkce
DWORD ExternalQueryInterface(
const void FAR* lpIID,
LPVOID FAR* ppvObj
);
Poznámky
Parametry
lpIID
Vzdálenější ukazatel na IID (první argument funkce QueryInterface)ppvObj
Ukazatel na rozhraní IUnknown* (druhý argument funkce QueryInterface)
Poznámky
Volejte tuto funkce ve vaší implementaci IUnknown pro každé rozhraní, které vaše třída implementuje.Tato funkce poskytuje standardní datovou implementaci QueryInterface na základě mapy rozhraní objektu.Je nezbytné převést vrácenou hodnotu na HRESULT.Pokud objekt je agregován, tato funkce bude volat "řízení IUnknown" bez nutnosti použít místní mapu rozhraní.
CCmdTarget::ExternalAddRef – Popis funkce
DWORD ExternalAddRef();
Poznámky
Volejte tuto funkce ve vaší implementaci IUnknown::AddRef pro každé rozhraní, které vaše třída implementuje.Vrácená hodnota je nový počet odkazů na objekt CCmdTarget.Pokud objekt je agregován, tato funkce bude volat "řízení IUnknown" bez nutnosti manipulovat počet místních odkazů.
CCmdTarget::ExternalRelease – Popis funkce
DWORD ExternalRelease();
Poznámky
Volejte tuto funkce ve vaší 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 objekt je agregován, tato funkce bude volat "řízení IUnknown" bez nutnosti manipulovat počet místních odkazů.
DECLARE_INTERFACE_MAP – Popis makra
DECLARE_INTERFACE_MAP
Poznámky
Použijte toto makro v libovolné třídě odvozené z CCmdTarget, která bude mít mapování rozhraní.Použít zcela stejným způsobem jako DECLARE_MESSAGE_MAP.Vyvolání toto makra by mělo být umístěno v definici třídy, obvykle v souboru záhlaví (.H).Třída s DECLARE_INTERFACE_MAP musí definovat mapování rozhraní v implementačním souboru (.CPP) s makry BEGIN_INTERFACE_MAP a END_INTERFACE_MAP.
BEGIN_INTERFACE_PART a END_INTERFACE_PART – popisy makra
BEGIN_INTERFACE_PART(
localClass,
iface
);
END_INTERFACE_PART(
localClass
)
Poznámky
Parametry
localClass
Název třídy, která implementuje rozhraníiface
Název rozhraní, které implementuje tato třída
Poznámky
Pro každé rozhraní, které implementuje vaše třída, je nutné mít pár BEGIN_INTERFACE_PART a END_INTERFACE_PART.Tato makra definují lokální třídu odvozenou z rozhraní OLE, které definujete, a z vložené členské proměnné této třídy.Členové AddRef, Verze a QueryInterface jsou deklarováni automaticky.Musíte zahrnout deklarace ostatních funkcí členů, které jsou součástí implementovaného rozhraní (tyto deklarace jsou umístěny mezi makrem BEGIN_INTERFACE_PART a END_INTERFACE_PART).
Argument iface představuje rozhraní OLE, které chcete implementovat, jako je IAdviseSink nebo IPersistStorage (nebo vlastní rozhraní).
Argument localClass je název místní třídy, která bude určena."X" bude automaticky předsunuto před název.Tato konvence pojmenování se používá pro zabránění kolizím s globálními třídami se stejným názvem.Navíc název vloženého člena, stejný jako název localClass, kromě toho, že mu předchází "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)
bude definovat místní třídu nazvanou MyAdviseSink z IAdviseSink a členem třídy, ve které je deklarován, nazvané m_xMyAdviseSink. Poznámka:
[!POZNÁMKA]
Řádky začínající textem STDMETHOD_ jsou v podstatě kopírovány z OLE2.H a mírně upraveny.Kopírování z OLE2.H může snížit chyby, které se těžko řeší.
BEGIN_INTERFACE_MAP a END_INTERFACE_MAP – popisy makra
BEGIN_INTERFACE_MAP(
theClass,
baseClass
)
END_INTERFACE_MAP
Poznámky
Parametry
theClass
Třída, ve které se má definovat mapa rozhraníbaseClass
Třída, ze které je theClass odvozen.
Poznámky
Makra BEGIN_INTERFACE_MAP a END_INTERFACE_MAP slouží skutečnou definici mapy rozhraní v souboru implementace.Pro každé rozhraní, které je implementováno, dojde k jednomu nebo více vyvoláním makra INTERFACE_PART.Pro každou agregaci, která používá třídu, je jedno volání makra INTERFACE_AGGREGATE.
INTERFACE_PART — Popis makra
INTERFACE_PART(
theClass,
iid,
localClass
)
Poznámky
Parametry
theClass
Název třídy, která obsahuje mapu rozhraní.iid
IID je třeba namapovat na vloženou třídu.localClass
Název místní třídy (menší než "X").
Poznámky
Toto makro se používá mezi makrem BEGIN_INTERFACE_MAP a makrem END_INTERFACE_MAP pro každé rozhraní, které bude váš objekt podporovat.Umožňuje mapovat IID k členovi třídy podle theClass a localClass.Do localClass bude automaticky přidáno 'm_x'.Všimněte si, že více než jeden IID může být spojen s jeden člen.To je velmi užitečné, když implementujete pouze "nejvíce odvozené" rozhraní a chcete poskytnout také všechna mezilehlá rozhraní.Dobrým příkladem je rozhraní IOleInPlaceFrameWindow.Jeho hierarchie vypadá takto:
IUnknown
IOleWindow
IOleUIWindow
IOleInPlaceFrameWindow
Pokud objekt implementuje IOleInPlaceFrameWindow, klient může QueryInterface na kterékoli z těchto rozhraní: IOleUIWindow, IOleWindow nebo IUnknown, kromě "nejvíce odvozeného" rozhraní IOleInPlaceFrameWindow (to, které právě implementujete).Ke zvládnutí této situace můžete použít více než jedno makro INTERFACE_PART k namapování každého základního rozhraní na rozhraní IOleInPlaceFrameWindow:
v souboru definice 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
Rámec pečuje o IUnknown, protože je vždy vyžadován.
INTERFACE_PART — Popis makra
INTERFACE_AGGREGATE(
theClass,
theAggr
)
Poznámky
Parametry
theClass
Název třídy, která obsahuje mapu rozhraní,theAggr
Název členské proměnné, která má být agregována.
Poznámky
Toto makro používá k informování rozhraní framework, že třída používá agregovaný objekt.Musí být mezi makry BEGIN_INTERFACE_PART a END_INTERFACE_PART makra.Agregovaný objekt je samostatný objekt, odvozený z rozhraní IUnknown.Pomocí agregace a makra INTERFACE_AGGREGATE můžete nastavit všechna rozhraní, která agregace podporuje, aby byla zdánlivě přímo podporována objektem.Argument theAggr je jednoduše název proměnné člena vaší třídy, který je odvozen z IUnknown (přímo nebo nepřímo).Všechna makra INTERFACE_AGGREGATE musí odpovídat makrům INTERFACE_PART při umístění v mapě rozhraní.