COM_INTERFACE_ENTRY and COM_INTERFACE_ENTRY_IID
#define COM_INTERFACE_ENTRY_IID(iid, x) \ { &iid, offsetofclass(x, _ComMapClass), _ATL_SIMPLEMAPENTRY},
COM_INTERFACE_ENTRY_IID的作用是消除多重继承的二义性
例如:
interface IGlobe : ISphere {}; interface IPlanet : ISphere {};
编译器无法得知ISphere应该指什么:
class CDesktopGlobe : public CComObjectRootEx<CDesktopGlobe>, public IGlobe, public IPlanet { public: ... BEGIN_COM_MAP(CDesktopGlobe) COM_INTERFACE_ENTRY(ISphere) // ambiguous COM_INTERFACE_ENTRY(IGlobe) COM_INTERFACE_ENTRY(IPlanet) END_COM_MAP() // ISphere methods ... // IGlobe methods ... // IPlanet methods ... };
改进的做法是,但是这带来了顺序问题,如果用户先调用IGlobe然后获取ISphere,用户接着访问IPlanet获取的ISphere得到的行为是不同的:
class CDesktopGlobe :
public CComObjectRootEx<CDesktopGlobe>,
public IGlobe,
public IPlanet {
public:
...
BEGIN_COM_MAP(CDesktopGlobe)
COM_INTERFACE_ENTRY_IID(IID_ISphere, IGlobe) // unambiguous
COM_INTERFACE_ENTRY(IGlobe)
COM_INTERFACE_ENTRY(IPlanet)
END_COM_MAP()
...
};
COM_INTERFACE_ENTRY2 and COM_INTERFACE_ENTRY2_IID
#define COM_INTERFACE_ENTRY2(x, x2)\ { &_ATL_IIDOF(x),\ reinterpret_cast<DWORD_PTR>( \ static_cast<x*>( \ static_cast<x2*>( \ reinterpret_cast<_ComMapClass*>(8))))-8, \ _ATL_SIMPLEMAPENTRY}, #define COM_INTERFACE_ENTRY2_IID(iid, x, x2)\ { &iid,\ reinterpret_cast<DWORD_PTR>( \ static_cast<x*>( \ static_cast<x2*>( \ reinterpret_cast<_ComMapClass*>(8))))-8, \ _ATL_SIMPLEMAPENTRY},
COM_INTERFACE_ENTRY2所做的工作和COM_INTERFACE_ENTRY_IID基本类似,例如:
class CDesktopGlobe :
public CComObjectRootEx<CDesktopGlobe>,
public IGlobe,
public IPlanet {
public:
...
BEGIN_COM_MAP(CDesktopGlobe)
COM_INTERFACE_ENTRY2(ISphere, IGlobe) // Use the IGlobal branch
COM_INTERFACE_ENTRY(IGlobe)
COM_INTERFACE_ENTRY(IPlanet)
END_COM_MAP()
...
};
COM_INTERFACE_ENTRY2_IID则可以确定IID和interface,例如:
class CDesktopGlobe :
public CComObjectRootEx<CDesktopGlobe>,
public IGlobe,
public IPlanet {
public:
...
BEGIN_COM_MAP(CDesktopGlobe)
COM_INTERFACE_ENTRY2_IID(&IID_ISphere, ISphere, IGlobe)
COM_INTERFACE_ENTRY(IGlobe)
COM_INTERFACE_ENTRY(IPlanet)
END_COM_MAP()
...
};
COM_INTERFACE_ENTRY_IMPL and COM_INTERFACE_ENTRY_IMPL_IID
#define COM_INTERFACE_ENTRY_IMPL(x) \ COM_INTERFACE_ENTRY_IID(_ATL_IIDOF(x), x##Impl<_ComMapClass>) #define COM_INTERFACE_ENTRY_IMPL_IID(iid, x) \ COM_INTERFACE_ENTRY_IID(iid, x##Impl<_ComMapClass>)
该宏基本被淘汰
COM_INTERFACE_ENTRY_TEAR_OFF
该类可以用于不常用的功能,以期减少内存开销
#define COM_INTERFACE_ENTRY_TEAR_OFF(iid, x) \ { &iid, \ (DWORD_PTR)&ATL::_CComCreatorData< \ ATL::CComInternalCreator< ATL::CComTearOffObject< x > > \ >::data, \ _Creator },
例如:
class CBeachBall : public CComObjectRootEx<CBeachBall>, public ISphere, public IRollableObject, public IPlaything, //public ILethalObject, // Implemented by the tear-off public ITakeUpSpace, public IWishIWereMoreUseful, public ITryToBeHelpful, public IAmDepressed { public: BEGIN_COM_MAP(CBeachBall) COM_INTERFACE_ENTRY(ISphere) COM_INTERFACE_ENTRY(IRollableObject) COM_INTERFACE_ENTRY(IPlaything) COM_INTERFACE_ENTRY_TEAR_OFF(IID_ILethalObject, CBeachBallLethalness) COM_INTERFACE_ENTRY(ITakeUpSpace) COM_INTERFACE_ENTRY(IWishIWereMoreUseful) COM_INTERFACE_ENTRY(ITryToBeHelpful) COM_INTERFACE_ENTRY(IAmDepressed) END_COM_MAP() ... private: GAS_TYPE m_gasFill; void HoldNearOpenFlame(); // Tear-offs are generally friends friend class CBeachBallLethalness; };
应该注意:
tear-off目的是不常用的接口
tear-off不能用于跨套间
tear-off实现类不能含自己的state,如果想实现per-interface tearoff state,使用cached tear-off
COM_INTERFACE_ENTRY_CACHED_TEAR_OFF
#define COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(iid, x, punk) \ { &iid, \ (DWORD_PTR)&ATL::_CComCacheData< \ ATL::CComCreator< ATL::CComCachedTearOffObject< x > >, \ (DWORD_PTR)offsetof(_ComMapClass, punk) >::data, \ _Cache },
例子:
class CBeachBall : public CComObjectRootEx<CBeachBall>, public ISphere, public IRollableObject, public IPlaything { public: BEGIN_COM_MAP(CBeachBall) COM_INTERFACE_ENTRY(ISphere) COM_INTERFACE_ENTRY(IRollableObject) COM_INTERFACE_ENTRY(IPlaything) COM_INTERFACE_ENTRY_TEAR_OFF(IID_ILethalObject, CBeachBallLethalness) COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(IID_ITakeUpSpace, CBeachBallAttitude, m_spunkAttitude.p) COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(IID_IWishIWereMoreUseful, CBeachBallAttitude, m_spunkAttitude.p) COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(IID_ITryToBeHelpful, CBeachBallAttitude, m_spunkAttitude.p) COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(IID_IAmDepressed, CBeachBallAttitude, m_spunkAttitude.p) END_COM_MAP() DECLARE_GET_CONTROLLING_UNKNOWN() // See the Aggregation section ... public: CComPtr<IUnknown> m_spunkAttitude; };
COM_INTERFACE_ENTRY_AGGREGATE and COM_INTERFACE_ENTRY_AGGREGATE_BLIND
#define COM_INTERFACE_ENTRY_AGGREGATE(iid, punk) \ { &iid, (DWORD_PTR)offsetof(_ComMapClass, punk), _Delegate }, #define COM_INTERFACE_ENTRY_AGGREGATE_BLIND(punk) \ { NULL, (DWORD_PTR)offsetof(_ComMapClass, punk), _Delegate},
例子:
class CBeachBall : public CComObjectRootEx<CBeachBall>, public ISphere, public IRollableObject, public IPlaything { public: BEGIN_COM_MAP(CBeachBall) COM_INTERFACE_ENTRY(ISphere) COM_INTERFACE_ENTRY(IRollableObject) COM_INTERFACE_ENTRY(IPlaything) COM_INTERFACE_ENTRY_AGGREGATE(IID_ILethalObject, m_spunkLethalness) COM_INTERFACE_ENTRY_AGGREGATE_BLIND(m_spunkAttitude) END_COM_MAP() DECLARE_GET_CONTROLLING_UNKNOWN() DECLARE_PROTECT_FINAL_CONSTRUCT() HRESULT FinalConstruct() { HESULT hr; hr = CoCreateInstance(CLSID_Lethalness, GetControllingUnknown(), CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&m_spunkLethalness); if( SUCCEEDED(hr) ) { hr = CoCreateInstance(CLSID_Attitude, GetControllingUnknown(), CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&m_spunkAttitude); } return hr; } void FinalRelease() { m_spunkLethalness.Release(); m_spunkAttitude.Release(); } ... public: CComPtr<IUnknown> m_spunkLethalness; CComPtr<IUnknown> m_spunkAttitude; };
注意上面要在FinalXXX中创建和消灭对象,并且需要定义DECLARE_GET_CONTROLLING_UNKNOWN()宏
#define DECLARE_GET_CONTROLLING_UNKNOWN() public: \ virtual IUnknown* GetControllingUnknown() { \ return GetUnknown(); }
COM_INTERFACE_ENTRY_AUTOAGGREGATE and COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND
#define COM_INTERFACE_ENTRY_AUTOAGGREGATE(iid, punk, clsid) \ { &iid, \ (DWORD_PTR)&ATL::_CComCacheData< \ ATL::CComAggregateCreator<_ComMapClass, &clsid>, \ (DWORD_PTR)offsetof(_ComMapClass, punk)>::data, \ _Cache }, #define COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND(punk, clsid) \ { NULL, \ (DWORD_PTR)&ATL::_CComCacheData< \ ATL::CComAggregateCreator<_ComMapClass, &clsid>, \ (DWORD_PTR)offsetof(_ComMapClass, punk)>::data, \ _Cache },
用了Auto,不再需要在FinalXXX中创建和销毁,但仍然需要定义DECLARE_GET_CONTROLLING_UNKNOWN()宏,例子:
class CBeachBall : public CComObjectRootEx<CBeachBall>, public ISphere, public IRollableObject, public IPlaything { public: BEGIN_COM_MAP(CBeachBall) COM_INTERFACE_ENTRY(ISphere) COM_INTERFACE_ENTRY(IRollableObject) COM_INTERFACE_ENTRY(IPlaything) COM_INTERFACE_ENTRY_AUTOAGGREGATE(IID_ILethalObject, m_spunkLethalness, CLSID_Lethalness) COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND(m_spunkAttitude, CLSID_Attitude) END_COM_MAP() DECLARE_GET_CONTROLLING_UNKNOWN() void FinalRelease() { m_spunkLethalness.Release(); m_spunkAttitude.Release(); } ... public: CComPtr<IUnknown> m_spunkLethalness; CComPtr<IUnknown> m_spunkAttitude; };
COM_INTERFACE_ENTRY_CHAIN
#define COM_INTERFACE_ENTRY_CHAIN(classname) \ { NULL, (DWORD_PTR)&ATL::_CComChainData<classname, \ _ComMapClass>::data, _Chain },
使用该宏不再需要一个一个定义IID和接口,例子:
class CBigBadBeachBall :
public CBeachBall,
public IBigObject,
public IBadObject {
public:
BEGIN_COM_MAP(CBigBadBeachBall)
COM_INTERFACE_ENTRY(IBigObject)
COM_INTERFACE_ENTRY_CHAIN(CBeachBall)
COM_INTERFACE_ENTRY(IBadObject)
END_COM_MAP()
...
};
trick:由于interface map首项的特殊性,不可以一上来就chain,如果找不到合适的首项,可以考虑IUnknown,例子:
class CBetterBeachBall : public CBeachBall { public: BEGIN_COM_MAP(CBetterBeachBall) COM_INTERFACE_ENTRY(IUnknown) COM_INTERFACE_ENTRY_CHAIN(CBeachBall) END_COM_MAP() ... };
COM_INTERFACE_ENTRY_NOINTERFACE
#define COM_INTERFACE_ENTRY_NOINTERFACE(x) \ { &_ATL_IIDOF(x), NULL, _NoInterface },
有时类查询需要做减法,如果你不想用户看到某个接口,或者在做了blind或chaining后去掉某个功能
例子:
class CBigNiceBeachBall :
public CBeachBall,
public IBigObject {
public:
BEGIN_COM_MAP(CBigNiceBeachBall)
COM_INTERFACE_ENTRY(IBigObject)
COM_INTERFACE_ENTRY_NOINTERFACE(ILethalObject)
COM_INTERFACE_ENTRY_CHAIN(CBeachBall)
END_COM_MAP()
...
};
COM_INTERFACE_ENTRY_BREAK
#define COM_INTERFACE_ENTRY_BREAK(x) \ { &_ATL_IIDOF(x), NULL, _Break },
如果发现某个接口异常,可以用这个宏,当访问该接口时会自动break
COM_INTERFACE_ENTRY_FUNC and COM_INTERFACE_ENTRY_FUNC_BLIND
用户可以通过这个宏自定义查表函数,扩展interface map功能,详见原书