接口着色--就是对于两个布局相同的接口的调用是兼容的。
如下:ISphere和IRedSphere布局相同,vptr table相同。
struct ISphere : IUnknown {
STDMETHOD(Rotate)(long nDegrees, long* pnOrientation) =0;
STDMETHOD(Twirl)(long nVelocity) =0;
};
struct IRedSphere {
// Colored IUnknown methods
STDMETHOD(RedQueryInterface)( REFIID riid, void** ppv) =0;
STDMETHOD_(ULONG, RedAddRef)() =0;
STDMETHOD_(ULONG, RedRelease)() =0;
// Uncolored ISphere methods
STDMETHOD(Rotate)(long nDegrees, long* pnOrientation) =0;
STDMETHOD(Twirl)(long nVelocity) =0;
};
实现组件时使用了IRedSphere接口进行实现。
class CDesktopGlobe :
public CComObjectRootEx <CComSingleThreadModel> ,
public IRedSphere,
public IGlobe,
public IPlanet {
public:
...
BEGIN_COM_MAP(CDesktopGlobe)
// Expose IRedShere when ISphere is requested
COM_INTERFACE_ENTRY_IID(IID_ISphere, IRedSphere) <=== 当查询ISphere接口时,返回IRedSphere接口。
COM_INTERFACE_ENTRY(IGlobe)
COM_INTERFACE_ENTRY(IPlanet)
END_COM_MAP()
...
// Colored method implementations
STDMETHODIMP RedQueryInterface(REFIID riid, void** ppv)
{ return GetUnknown()-> QueryInterface(riid, ppv); }
STDMETHODIMP_(ULONG) RedAddRef() {
_ThreadModel::Increment(&m_cRefSphere);
return GetUnknown()-> AddRef();
}
STDMETHODIMP_(ULONG) RedRelease() {
_ThreadModel::Decrement(&m_cRefSphere);
return GetUnknown()-> Release();
}
private:
long m_cRefSphere;
};
这样,客户端对于查询ISphere接口时返回IRedSphere接口毫不知晓,一样使用。
只是这时实际调用的是IRedSphere实现。
void TryRotate(IUnknown* punk) {
ISphere* ps = 0;
// Implicit AddRef really a call to RedAddRef
if(SUCCEEDED(punk-> QueryInterface(IID_ISphere, (void**)&ps))) { <=== 实际返回的是IRedSphere接口
// ps actually points to an IRedSphere*
ps-> Rotate(); <=== IRedSphere实现
ps-> Release(); // Really a call to RedRelease
}
}
比较COM_INTERFACE_ENTRY_IID(IID_ISphere, IRedSphere)和
COM_INTERFACE_ENTRY_IID(IDispatch, IUse2)
两者有异曲同工之妙!都是呼叫一个接口,返回另外一个接口.
只不过IUse2和IDispatch具有继承关系,而ISphere和IRedSphere接口布局一致.
总之,能这么作的根本是接口兼容,或可以互相转化.