在ATL中,类厂是计数的。1. CComCoClass
template <class T, const CLSID* pclsid = &CLSID_NULL>
class CComCoClass
{
public:
DECLARE_CLASSFACTORY()...
}2. 
#define DECLARE_CLASSFACTORY() DECLARE_CLASSFACTORY_EX(ATL::CComClassFactory)3. 类厂的_ClassFactoryCreatorClass
#if defined(_WINDLL) | defined(_USRDLL)
#define DECLARE_CLASSFACTORY_EX(cf) typedef ATL::CComCreator< ATL::CComObjectCached< cf > > _ClassFactoryCreatorClass;
#else
// don't let class factory refcount influence lock count
#define DECLARE_CLASSFACTORY_EX(cf) typedef ATL::CComCreator< ATL::CComObjectNoLock< cf > > _ClassFactoryCreatorClass;
#endif4.1 对于in-pro
template <class Base>
class CComObjectCached : public Base
{
public:
typedef Base _BaseClass;
CComObjectCached(void* = NULL){}
// Set refcount to -(LONG_MAX/2) to protect destruction and 
// also catch mismatched Release in debug builds
~CComObjectCached()
{
m_dwRef = -(LONG_MAX/2);
FinalRelease();
#ifdef _ATL_DEBUG_INTERFACES
_AtlDebugInterfacesModule.DeleteNonAddRefThunk(_GetRawUnknown());
#endif
}
//If InternalAddRef or InternalRelease is undefined then your class
//doesn't derive from CComObjectRoot
STDMETHOD_(ULONG, AddRef)()
{
m_csCached.Lock();
ULONG l = InternalAddRef();
if (m_dwRef == 2)
_pAtlModule->Lock();
m_csCached.Unlock();
return l;
}
STDMETHOD_(ULONG, Release)()
{
m_csCached.Lock();
InternalRelease();
ULONG l = m_dwRef;
m_csCached.Unlock();
if (l == 0)
delete this;
else if (l == 1)
_pAtlModule->Unlock();
return l;
}
//if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{return _InternalQueryInterface(iid, ppvObject);}
CComGlobalsThreadModel::AutoCriticalSection m_csCached;
};4.2 对于local server or service
template <class Base>
class CComObjectNoLock : public Base
{
public:
typedef Base _BaseClass;
CComObjectNoLock(void* = NULL){}
// Set refcount to -(LONG_MAX/2) to protect destruction and 
// also catch mismatched Release in debug builds
~CComObjectNoLock()
{
m_dwRef = -(LONG_MAX/2);
FinalRelease();
#ifdef _ATL_DEBUG_INTERFACES
_AtlDebugInterfacesModule.DeleteNonAddRefThunk(_GetRawUnknown());
#endif
} //If InternalAddRef or InternalRelease is undefined then your class
//doesn't derive from CComObjectRoot
STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
STDMETHOD_(ULONG, Release)()
{
ULONG l = InternalRelease();
if (l == 0)
delete this;
return l;
}
//if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{return _InternalQueryInterface(iid, ppvObject);}
};由此可见,在ATL中类厂是计数的。
只不过对于in-pro, 只有当m_dwRef由1->2时,才lock server;当m_dwRef由2->1时,就unlock server;通过这种手段来避免“自相矛盾”。而对于local server or service, 计数但不lock/unlock server.

解决方案 »

  1.   

    以下是分别基于虚函数和模版方式实现的多态=================== 虚函数 =====================
    #include <iostream>
    using namespace std;class Base {
    public:
    virtual void fun() {
    cout << "Base::fun" << endl;
    } void doSomething() {
    fun();
    }
    };class Drive : public Base {
    public:
    void fun() {
    cout << "Drive::fun" << endl;
    }
    };int main() {
    Drive obj;
    obj.doSomething(); return 0;
    }=================== 模版 =====================#include <iostream>
    using namespace std;template <typename T>
    class Base {
    public:
    void fun() {
    cout << "Base::fun" << endl;
    } void doSomething() {
    T* pT = static_cast<T*>(this);
    pT->fun();
    }
    };class Drive : public Base<Drive> {
    public:
    void fun() {
    cout << "Drive::fun" << endl;
    }
    };int main() {
    Drive obj;
    obj.doSomething(); return 0;
    }
      

  2.   

    T* pT = static_cast<T*>(this);
    将子类以模版参数的形式传给基类,然后将基类"逆"转换为子类. 如果子类覆盖了原来基类的函数,就执行子类的函数---实现多态.
      

  3.   

    ATL是通过接口映射表来实现QueryInterface的。1. 从CComObject, CComObjectCached, CComAggObject 等中可以看出,QueryInterface都是_InternalQueryInterface实现的。template <class Base>
    class CComObject : public Base
    {
    public:
          ... //if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
    STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) throw()
    {return _InternalQueryInterface(iid, ppvObject);}
    ...
    };template <class contained>
    class CComAggObject :
    public IUnknown,
    public CComObjectRootEx< contained::_ThreadModel::ThreadModelNoCS >
    {
    public:
    STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
    {
    ATLASSERT(ppvObject != NULL);
    if (ppvObject == NULL)
    return E_POINTER;
    *ppvObject = NULL; HRESULT hRes = S_OK;
    if (InlineIsEqualUnknown(iid))
    {
    *ppvObject = (void*)(IUnknown*)this;
    AddRef();
    #ifdef _ATL_DEBUG_INTERFACES
    _AtlDebugInterfacesModule.AddThunk((IUnknown**)ppvObject, (LPCTSTR)contained::_GetEntries()[-1].dw, iid);
    #endif // _ATL_DEBUG_INTERFACES
    }
    else
    hRes = m_contained._InternalQueryInterface(iid, ppvObject);
    return hRes;
    }
    }2. 而_InternalQueryInterface 是定义于宏BEGIN_COM_MAP中的,并且调用了CComObjectRootBase中的InternalQueryInterface。其中包含参数_GetEntries() ---这是接口映射表、#define BEGIN_COM_MAP(x) public: \
    .....
    HRESULT _InternalQueryInterface(REFIID iid, void** ppvObject) throw() \
    { return InternalQueryInterface(this, _GetEntries(), iid, ppvObject); } \
    const static ATL::_ATL_INTMAP_ENTRY* WINAPI _GetEntries() throw() { \
    static const ATL::_ATL_INTMAP_ENTRY _entries[] = { DEBUG_QI_ENTRY(x)3. InternalQueryInterface由调用AtlInternalQueryInterface来遍历口映射表来查找接口,并获得接口指针 static HRESULT WINAPI InternalQueryInterface(void* pThis,
    const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)
    {
    // Only Assert here. AtlInternalQueryInterface will return the correct HRESULT if ppvObject == NULL
    #ifndef _ATL_OLEDB_CONFORMANCE_TESTS
    ATLASSERT(ppvObject != NULL);
    #endif
    ATLASSERT(pThis != NULL);
    // First entry in the com map should be a simple map entry
    ATLASSERT(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);
    #if defined(_ATL_DEBUG_INTERFACES) || defined(_ATL_DEBUG_QI)
    LPCTSTR pszClassName = (LPCTSTR) pEntries[-1].dw;
    #endif // _ATL_DEBUG_INTERFACES
    HRESULT hRes = AtlInternalQueryInterface(pThis, pEntries, iid, ppvObject);
    #ifdef _ATL_DEBUG_INTERFACES
    _AtlDebugInterfacesModule.AddThunk((IUnknown**)ppvObject, pszClassName, iid);
    #endif // _ATL_DEBUG_INTERFACES
    return _ATLDUMPIID(iid, pszClassName, hRes);
    }
    4。而接口映射表是通过一组宏来实现的BEGIN_COM_MAP(CClass)
    COM_INTERFACE_ENTRY(IA)
    COM_INTERFACE_ENTRY(IB)
    END_COM_MAP()// BEGIN_COM_MAP
    #define BEGIN_COM_MAP(x) public: \
    .....
    HRESULT _InternalQueryInterface(REFIID iid, void** ppvObject) throw() \
    { return InternalQueryInterface(this, _GetEntries(), iid, ppvObject); } \
    const static ATL::_ATL_INTMAP_ENTRY* WINAPI _GetEntries() throw() { \
    static const ATL::_ATL_INTMAP_ENTRY _entries[] = { DEBUG_QI_ENTRY(x)// COM_INTERFACE_ENTRY
    #define COM_INTERFACE_ENTRY(x)\
    {&_ATL_IIDOF(x), \
    offsetofclass(x, _ComMapClass), \
    _ATL_SIMPLEMAPENTRY},// END_COM_MAP
    #ifdef _ATL_DEBUG
    #define END_COM_MAP() \
    __if_exists(_GetAttrEntries) {{NULL, (DWORD_PTR)_GetAttrEntries, _ChainAttr }, }\
    {NULL, 0, 0}}; return &_entries[1];} \
    virtual ULONG STDMETHODCALLTYPE AddRef( void) throw() = 0; \
    virtual ULONG STDMETHODCALLTYPE Release( void) throw() = 0; \
    STDMETHOD(QueryInterface)(REFIID, void**) throw() = 0;
    #else
    #define END_COM_MAP() \
    __if_exists(_GetAttrEntries) {{NULL, (DWORD_PTR)_GetAttrEntries, _ChainAttr }, }\
    {NULL, 0, 0}}; return _entries;} \
    virtual ULONG STDMETHODCALLTYPE AddRef( void) throw() = 0; \
    virtual ULONG STDMETHODCALLTYPE Release( void) throw() = 0; \
    STDMETHOD(QueryInterface)(REFIID, void**) throw() = 0;
    #endif // _ATL_DEBUG5. AtlInternalQueryInterface实现
    ATLINLINE ATLAPI AtlInternalQueryInterface(void* pThis,
    const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)
    {
    ATLASSERT(pThis != NULL);
    // First entry in the com map should be a simple map entry
    ATLASSERT(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);
    if (ppvObject == NULL)
    return E_POINTER;
    *ppvObject = NULL;
    if (InlineIsEqualUnknown(iid)) // use first interface
    {
    IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries->dw);
    pUnk->AddRef();
    *ppvObject = pUnk;
    return S_OK;
    }
    while (pEntries->pFunc != NULL)
    {
    BOOL bBlind = (pEntries->piid == NULL);
    if (bBlind || InlineIsEqualGUID(*(pEntries->piid), iid))
    {
    if (pEntries->pFunc == _ATL_SIMPLEMAPENTRY) //offset
    {
    ATLASSERT(!bBlind);
    IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries->dw);
    pUnk->AddRef();
    *ppvObject = pUnk;
    return S_OK;
    }
    else //actual function call
    {
    HRESULT hRes = pEntries->pFunc(pThis,
    iid, ppvObject, pEntries->dw);
    if (hRes == S_OK || (!bBlind && FAILED(hRes)))
    return hRes;
    }
    }
    pEntries++;
    }
    return E_NOINTERFACE;
    }ATL实际是通过计算指针偏移量来获得指针的。
    IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries->dw);
      

  4.   

    多继承情况下接口的取得。1. 多Dispatch接口
    class ATL_NO_VTABLE CUse : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CUse, &CLSID_Parser>,
    public IDispatchImpl<IUse1, &IID_IParser, &LIBID_netfeesrvLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
    public IDispatchImpl<IUse1, &IID_IParser, &LIBID_netfeesrvLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
    {
    public: ....
    }2. 接口分布结构:
    pI -----> vptr1 ------> IUnkonwn
       |                    IDispatch
       dw                   IUse1
       |
        ----> vptr2 ------> IUnkonwn
                    IDispatch
            IUse23. 接口映射表
    BEGIN_COM_MAP(CUse)
    COM_INTERFACE_ENTRY(IUse1)
    COM_INTERFACE_ENTRY(IUse2)
    COM_INTERFACE_ENTRY_IID(IDispatch, IUse2)
    END_COM_MAP()#define COM_INTERFACE_ENTRY(x)\
    {&_ATL_IIDOF(x), \
    offsetofclass(x, _ComMapClass), \
    _ATL_SIMPLEMAPENTRY},#define COM_INTERFACE_ENTRY_IID(iid, x)\
    {&iid,\
    offsetofclass(x, _ComMapClass),\
    _ATL_SIMPLEMAPENTRY},IUse1 ---- offsetofclass(IUse1, _ComMapClass) ---_ATL_SIMPLEMAPENTRY
    IUse2 ---- offsetofclass(IUse2, _ComMapClass) ---_ATL_SIMPLEMAPENTRY
    IDispatch -- offsetofclass(IUse2, _ComMapClass) ---_ATL_SIMPLEMAPENTRY <======= 选择IUse2计算接口偏移4。
    ATLINLINE ATLAPI AtlInternalQueryInterface(void* pThis,
    const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)
    {
    ATLASSERT(pThis != NULL);
    // First entry in the com map should be a simple map entry
    ATLASSERT(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);
    if (ppvObject == NULL)
    return E_POINTER;
    *ppvObject = NULL;
    if (InlineIsEqualUnknown(iid)) // use first interface
    {
    IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries->dw);
    pUnk->AddRef();
    *ppvObject = pUnk;
    return S_OK;
    }
    while (pEntries->pFunc != NULL)
    {
    BOOL bBlind = (pEntries->piid == NULL);
    if (bBlind || InlineIsEqualGUID(*(pEntries->piid), iid))
    {
    if (pEntries->pFunc == _ATL_SIMPLEMAPENTRY) //offset
    {
    ATLASSERT(!bBlind);
    IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries->dw);
    pUnk->AddRef();
    *ppvObject = pUnk;
    return S_OK;
    }
    else //actual function call
    {
    HRESULT hRes = pEntries->pFunc(pThis,
    iid, ppvObject, pEntries->dw);
    if (hRes == S_OK || (!bBlind && FAILED(hRes)))
    return hRes;
    }
    }
    pEntries++;
    }
    return E_NOINTERFACE;
    }通过
    IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries->dw);
    来通过偏移量获得接口指针 --- *ppvObject = pUnk;
    即:基类指针(pUnk)象子类指针(pIUser1, pIUser2)逆转换。
    pIUser1 = pUnk;
    pIUser2 = pUnk;当要取得IDispatch时,根据程序作出的选择 
    IDispatch -- offsetofclass(IUse2, _ComMapClass) ---_ATL_SIMPLEMAPENTRY <======= 选择IUse2计算接口偏移
    在这里就是计算出pIUse2的偏移量,取得接口指针 --- *ppvObject = pUnk;
    即:基类指针(pUnk)象子类指针(pIDispatch)逆转换。
    pIDispatch = pUnk;由此可以看出,当实现象IDispatch这样的多继承时,需要类设计者决定IDispatch的走向:
    IDispatch -- offsetofclass(IUse2, _ComMapClass) ---_ATL_SIMPLEMAPENTRY <======= 选择IUse2计算接口偏移
      

  5.   

    也就是说:
    在COM_INTERFACE_ENTRY_IID(IDispatch, IUse2)中:
    IDispatch决定最终获得接口的类型;
    而IUse2决定取得接口指针的偏移量
      

  6.   

    也就是说:
    在COM_INTERFACE_ENTRY_IID(IDispatch, IUse2)中:
    而IUse2决定取得接口指针的偏移量
    IDispatch决定最终获得接口的类型--由*ppvObject = pUnk作隐式转换;
      

  7.   

    接口着色--就是对于两个布局相同的接口的调用是兼容的。如下: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
      }
    }
      

  8.   

    比较COM_INTERFACE_ENTRY_IID(IID_ISphere, IRedSphere)和
    COM_INTERFACE_ENTRY_IID(IDispatch, IUse2) 
    两者有异曲同工之妙!都是呼叫一个接口,返回另外一个接口.
    只不过IUse2和IDispatch具有继承关系,而ISphere和IRedSphere接口布局一致.总之,能怎么作的根本是接口兼容.
      

  9.   

    比较COM_INTERFACE_ENTRY_IID(IID_ISphere, IRedSphere)和
    COM_INTERFACE_ENTRY_IID(IDispatch, IUse2) 
    两者有异曲同工之妙!都是呼叫一个接口,返回另外一个接口.
    只不过IUse2和IDispatch具有继承关系,而ISphere和IRedSphere接口布局一致.总之,能怎么作的根本是接口兼容,或可以互相转化.
      

  10.   

    对于HRESULT CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) throw(), 其中的pUnkOuter决定所创造的组件是独立的还是聚合的。 CoCreateInstance
    |
    |
                     pUnkOuter == NULL  -----yes-----> "new" CComObject<CClass>
    |
            no
    |
       "new" CComAggObject<CClass>这里的"new",其实就是CComCreator.CreateInstance(pv, riid, ppv);
    其中的pv = pUnkOuter ;template <class T1>
    class CComCreator
    {
    public:
    static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)
    {
    ATLASSERT(ppv != NULL);
    if (ppv == NULL)
    return E_POINTER;
    *ppv = NULL; HRESULT hRes = E_OUTOFMEMORY;
    T1* p = NULL;
    ATLTRY(p = new T1(pv))
    if (p != NULL)
    {
    p->SetVoid(pv);
    p->InternalFinalConstructAddRef();
    hRes = p->FinalConstruct();
    if (SUCCEEDED(hRes))
    hRes = p->_AtlFinalConstruct();
    p->InternalFinalConstructRelease();
    if (hRes == S_OK)
    hRes = p->QueryInterface(riid, ppv);
    if (hRes != S_OK)
    delete p;
    }
    return hRes;
    }
    };这个过程对于独立或聚合来说,没有什么大的区别。关于组件聚合,需要分两方面讨论:
    1。聚合别的组件(外部)class ATL_NO_VTABLE CComplexMath : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CComplexMath, &CLSID_ComplexMath>,
    public IDispatchImpl<IComplexMath, &IID_IComplexMath, &LIBID_COMPLEXCALCULATORLib>
    {
    public:
    CComplexMath()
    {
    }DECLARE_REGISTRY_RESOURCEID(IDR_COMPLEXMATH)DECLARE_PROTECT_FINAL_CONSTRUCT()BEGIN_COM_MAP(CComplexMath)
    COM_INTERFACE_ENTRY(IComplexMath)
    COM_INTERFACE_ENTRY_AGGREGATE (IID_ISimpleMath, ptrUnk)  <=== 增加要暴露的内部组件接口,到内部组件IUnknown接口的映射
    COM_INTERFACE_ENTRY(IDispatch)
    END_COM_MAP()// IComplexMath
    public:
    STDMETHOD(Divide)(/*[in]*/ int x, /*[in]*/ int y, /*[out,retval]*/ int* z );//Aggregation: Add the controlling macro, finalconstruct and final release
    DECLARE_GET_CONTROLLING_UNKNOWN() <=== 获取外部IUnknown接口指针
    IUnknown* ptrUnk; <=== 内部组件的IUnknown接口指针

    HRESULT FinalConstruct()
    {
       return     CoCreateInstance(CLSID_SimpleMath, GetControllingUnknown(),CLSCTX_ALL, IID_IUnknown, (void**)&ptrUnk); <=== 创建内部组件(引入pUnkOuter),并获得内部组件的IUnknown接口指针ptrUnk
    }

    void FinalRelease()
    {
     ptrUnk->Release(); <=== 释放内部组件的IUnknown接口指针
    }
    //End};聚合接口映射宏:
    COM_INTERFACE_ENTRY_AGGREGATE (IID_ISimpleMath, ptrUnk)展开为:
    #define COM_INTERFACE_ENTRY_AGGREGATE(iid, punk)\
    {&iid,\
    (DWORD_PTR)offsetof(_ComMapClass, punk),\ // dw中存储的是内部对象接口指针在外部对象中的偏移量
    _Delegate},ATL使用_Delegate来支持聚合。
    static HRESULT WINAPI _Delegate(void* pv, REFIID iid, void** ppvObject, DWORD_PTR dw)
    {
    HRESULT hRes = E_NOINTERFACE;
    IUnknown* p = *(IUnknown**)((DWORD_PTR)pv + dw);   // 通过偏移量得到内部接口指针
    if (p != NULL)
    hRes = p->QueryInterface(iid, ppvObject);  // 查询得到所要求的内部接口指针
    return hRes;
    }ATLINLINE ATLAPI AtlInternalQueryInterface(void* pThis,
    const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)
    {
    ATLASSERT(pThis != NULL);
    // First entry in the com map should be a simple map entry
    ATLASSERT(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);
    if (ppvObject == NULL)
    return E_POINTER;
    *ppvObject = NULL;
    if (InlineIsEqualUnknown(iid)) // use first interface
    {
    IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries->dw);
    pUnk->AddRef();
    *ppvObject = pUnk;
    return S_OK;
    }
    while (pEntries->pFunc != NULL)
    {
    BOOL bBlind = (pEntries->piid == NULL);
    if (bBlind || InlineIsEqualGUID(*(pEntries->piid), iid))
    {
    if (pEntries->pFunc == _ATL_SIMPLEMAPENTRY) //offset
    {
    ATLASSERT(!bBlind);
    IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries->dw);
    pUnk->AddRef();
    *ppvObject = pUnk;
    return S_OK;
    }
    else //actual function call
    {
    HRESULT hRes = pEntries->pFunc(pThis,    <== 使用_Delegate来支持聚合。 
    iid, ppvObject, pEntries->dw);
    if (hRes == S_OK || (!bBlind && FAILED(hRes)))
    return hRes;
    }
    }
    pEntries++;
    }
    return E_NOINTERFACE;
    }由此可见,接口映射表就像一个“路由器”---将不同的接口请求转发到外部或内部组件。BEGIN_COM_MAP(CComplexMath)
    COM_INTERFACE_ENTRY(IComplexMath)
    COM_INTERFACE_ENTRY_AGGREGATE (IID_ISimpleMath, ptrUnk)  <=== 增加要暴露的内部组件接口,到内部组件IUnknown接口的映射
    COM_INTERFACE_ENTRY(IDispatch)
    END_COM_MAP()2。被聚合的组件(内部)
    一个组件以被聚合形式存在时,最关键在于---pUnkOuter 的引入(p = new T1(pv))。被聚合的组件实现了两套:AddRef, Release 和 QueryInterface。一套用于内部控制;
    一套用于外部控制-- 通过pUnkOuter;
    template <class contained>
    class CComAggObject :
    public IUnknown,
    public CComObjectRootEx< contained::_ThreadModel::ThreadModelNoCS >
    {
    public:
    .............. STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();} // 内部
    STDMETHOD_(ULONG, Release)() // 内部
    {
    ULONG l = InternalRelease();
    if (l == 0)
    delete this;
    return l;
    }
    STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)  // 内部
    {
    ATLASSERT(ppvObject != NULL);
    if (ppvObject == NULL)
    return E_POINTER;
    *ppvObject = NULL; HRESULT hRes = S_OK;
    if (InlineIsEqualUnknown(iid))
    {
    *ppvObject = (void*)(IUnknown*)this;
    AddRef(); ......... }
    else
    hRes = m_contained._InternalQueryInterface(iid, ppvObject); // 
    return hRes;
    } ... CComContainedObject<contained> m_contained;
    };template <class Base> //Base must be derived from CComObjectRoot
    class CComContainedObject : public Base
    {
    public:
    typedef Base _BaseClass;
    CComContainedObject(void* pv) {m_pOuterUnknown = (IUnknown*)pv;}
    #ifdef _ATL_DEBUG_INTERFACES
    ~CComContainedObject()
    {
    _AtlDebugInterfacesModule.DeleteNonAddRefThunk(_GetRawUnknown());
    _AtlDebugInterfacesModule.DeleteNonAddRefThunk(m_pOuterUnknown);
    }
    #endif STDMETHOD_(ULONG, AddRef)() {return OuterAddRef();} // 外部
    STDMETHOD_(ULONG, Release)() {return OuterRelease();} // 外部
    STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) // 外部
    {
    return OuterQueryInterface(iid, ppvObject);
    }
    template <class Q>
    HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp)
    {
    return QueryInterface(__uuidof(Q), (void**)pp);
    }
    //GetControllingUnknown may be virtual if the Base class has declared
    //DECLARE_GET_CONTROLLING_UNKNOWN()
    IUnknown* GetControllingUnknown()
    {
    #ifdef _ATL_DEBUG_INTERFACES
    IUnknown* p;
    _AtlDebugInterfacesModule.AddNonAddRefThunk(m_pOuterUnknown, _T("CComContainedObject"), &p);
    return p;
    #else
    return m_pOuterUnknown;
    #endif
    }
    };
      

  11.   

    BEGIN_COM_MAP(CAdvance)
    COM_INTERFACE_ENTRY(IAdvance)
    COM_INTERFACE_ENTRY_AGGREGATE(IID_IMath1, m_pSimpleUnknown)----------
    COM_INTERFACE_ENTRY_AGGREGATE(IID_IMath2, m_pSimpleUnknown)------- 路由
    END_COM_MAP()      |
         |
         |
    (内部被聚合类)
    BEGIN_COM_MAP(CSimple)
    COM_INTERFACE_ENTRY(IID_IMath1)
    COM_INTERFACE_ENTRY(IID_IMath2)
    END_COM_MAP()
      

  12.   

    错误与异常处理---组件在组件程序中,如果遇到错误,一般有两个方式进行处理。
    1.简单返回HRESULT
      对于比较简单的错误,直接返回表示错误原因的 HRESULT。2.抛出COM异常---错误信息接口
    既然 COM 是靠各种各样的接口来提供服务的,于是很自然地就会想到,是否有一个接口能够提供更丰富的错误信息报告那?答案是:ISupportErrorInfo。ATL 把上述的代码给我们包装成 CComCoClass::Error() 的6个重载函数了。函数调用过程如下:
    Error --> AtlReportError --> AtlSetErrorInfo --> SetErrorInfo(0, pErrorInfo);template <class T, const CLSID* pclsid = &CLSID_NULL>
    class CComCoClass
    {
    public:
    DECLARE_CLASSFACTORY()
    DECLARE_AGGREGATABLE(T)
    typedef T _CoClass;
    static const CLSID& WINAPI GetObjectCLSID() {return *pclsid;}
    static LPCTSTR WINAPI GetObjectDescription() {return NULL;}
    static HRESULT WINAPI Error(LPCOLESTR lpszDesc, <=== Error
    const IID& iid = GUID_NULL, HRESULT hRes = 0)
    {
    return AtlReportError(GetObjectCLSID(), lpszDesc, iid, hRes);
    }
    static HRESULT WINAPI Error(LPCOLESTR lpszDesc, DWORD dwHelpID, <=== Error
    LPCOLESTR lpszHelpFile, const IID& iid = GUID_NULL, HRESULT hRes = 0)
    {
    return AtlReportError(GetObjectCLSID(), lpszDesc, dwHelpID, lpszHelpFile,
    iid, hRes);
    }
    static HRESULT WINAPI Error(UINT nID, const IID& iid = GUID_NULL, <=== Error
    HRESULT hRes = 0, HINSTANCE hInst = _AtlBaseModule.GetResourceInstance())
    {
    return AtlReportError(GetObjectCLSID(), nID, iid, hRes, hInst);
    }
    static HRESULT WINAPI Error(UINT nID, DWORD dwHelpID,
    LPCOLESTR lpszHelpFile, const IID& iid = GUID_NULL,
    HRESULT hRes = 0, HINSTANCE hInst = _AtlBaseModule.GetResourceInstance()) <=== Error
    {
    return AtlReportError(GetObjectCLSID(), nID, dwHelpID, lpszHelpFile,
    iid, hRes, hInst);
    }
    static HRESULT WINAPI Error(LPCSTR lpszDesc,
    const IID& iid = GUID_NULL, HRESULT hRes = 0) <=== Error
    {
    return AtlReportError(GetObjectCLSID(), lpszDesc, iid, hRes);
    }
    static HRESULT WINAPI Error(LPCSTR lpszDesc, DWORD dwHelpID, <=== Error
    LPCSTR lpszHelpFile, const IID& iid = GUID_NULL, HRESULT hRes = 0)
    {
    return AtlReportError(GetObjectCLSID(), lpszDesc, dwHelpID,
    lpszHelpFile, iid, hRes);
    } ...
    };--> AtlReportError:
    inline HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCOLESTR lpszDesc,
    const IID& iid = GUID_NULL, HRESULT hRes = 0)
    {
    return AtlSetErrorInfo(clsid, lpszDesc, 0, NULL, iid, hRes, NULL);
    }--> AtlSetErrorInfo:
    ATLINLINE ATLAPI AtlSetErrorInfo(const CLSID& clsid, LPCOLESTR lpszDesc, DWORD dwHelpID,
    LPCOLESTR lpszHelpFile, const IID& iid, HRESULT hRes, HINSTANCE hInst)
    {
    USES_CONVERSION;
    TCHAR szDesc[1024];
    szDesc[0] = NULL;
    // For a valid HRESULT the id should be in the range [0x0200, 0xffff]
    if (IS_INTRESOURCE(lpszDesc)) //id
    {
    UINT nID = LOWORD((DWORD_PTR)lpszDesc);
    ATLASSERT((nID >= 0x0200 && nID <= 0xffff) || hRes != 0);
    if (LoadString(hInst, nID, szDesc, 1024) == 0)
    {
    ATLASSERT(FALSE);
    lstrcpy(szDesc, _T("Unknown Error"));
    }
    lpszDesc = T2OLE(szDesc);
    if (hRes == 0)
    hRes = MAKE_HRESULT(3, FACILITY_ITF, nID);
    } CComPtr<ICreateErrorInfo> pICEI;
    if (SUCCEEDED(CreateErrorInfo(&pICEI)))
    {
    CComPtr<IErrorInfo> pErrorInfo;
    pICEI->SetGUID(iid);
    LPOLESTR lpsz;
    ProgIDFromCLSID(clsid, &lpsz);
    if (lpsz != NULL)
    pICEI->SetSource(lpsz);
    if (dwHelpID != 0 && lpszHelpFile != NULL)
    {
    pICEI->SetHelpContext(dwHelpID);
    pICEI->SetHelpFile(const_cast<LPOLESTR>(lpszHelpFile));
    }
    CoTaskMemFree(lpsz);
    pICEI->SetDescription((LPOLESTR)lpszDesc);
    if (SUCCEEDED(pICEI->QueryInterface(__uuidof(IErrorInfo), (void**)&pErrorInfo)))
    SetErrorInfo(0, pErrorInfo); <====== 抛出COM异常
    }
    return (hRes == 0) ? DISP_E_EXCEPTION : hRes;
    }
      

  13.   

    也就是,在组件实现中只要调用Error(...),就可以抛出COM异常(实际调用SetErrorInfo)。
      

  14.   

    错误与异常处理---组件在组件程序中,如果遇到错误,一般有两个方式进行处理。
    1.简单返回HRESULT
      对于比较简单的错误,直接返回表示错误原因的 HRESULT。2.抛出COM异常---调用Error(...)
    既然 COM 是靠各种各样的接口来提供服务的,于是很自然地就会想到,是否有一个接口能够提供更丰富的错误信息报告那?答案是:IErrorInfo(调用SetErrorInfo(0, pErrorInfo);)。ATL 把SetErrorInfo包装成 CComCoClass::Error() 的6个重载函数了。函数调用过程如下:
    Error --> AtlReportError --> AtlSetErrorInfo --> SetErrorInfo(0, pErrorInfo);template <class T, const CLSID* pclsid = &CLSID_NULL>
    class CComCoClass
    {
    public:
    DECLARE_CLASSFACTORY()
    DECLARE_AGGREGATABLE(T)
    typedef T _CoClass;
    static const CLSID& WINAPI GetObjectCLSID() {return *pclsid;}
    static LPCTSTR WINAPI GetObjectDescription() {return NULL;}
    static HRESULT WINAPI Error(LPCOLESTR lpszDesc, <=== Error
    const IID& iid = GUID_NULL, HRESULT hRes = 0)
    {
    return AtlReportError(GetObjectCLSID(), lpszDesc, iid, hRes);
    }
    static HRESULT WINAPI Error(LPCOLESTR lpszDesc, DWORD dwHelpID, <=== Error
    LPCOLESTR lpszHelpFile, const IID& iid = GUID_NULL, HRESULT hRes = 0)
    {
    return AtlReportError(GetObjectCLSID(), lpszDesc, dwHelpID, lpszHelpFile,
    iid, hRes);
    }
    static HRESULT WINAPI Error(UINT nID, const IID& iid = GUID_NULL, <=== Error
    HRESULT hRes = 0, HINSTANCE hInst = _AtlBaseModule.GetResourceInstance())
    {
    return AtlReportError(GetObjectCLSID(), nID, iid, hRes, hInst);
    }
    static HRESULT WINAPI Error(UINT nID, DWORD dwHelpID,
    LPCOLESTR lpszHelpFile, const IID& iid = GUID_NULL,
    HRESULT hRes = 0, HINSTANCE hInst = _AtlBaseModule.GetResourceInstance()) <=== Error
    {
    return AtlReportError(GetObjectCLSID(), nID, dwHelpID, lpszHelpFile,
    iid, hRes, hInst);
    }
    static HRESULT WINAPI Error(LPCSTR lpszDesc,
    const IID& iid = GUID_NULL, HRESULT hRes = 0) <=== Error
    {
    return AtlReportError(GetObjectCLSID(), lpszDesc, iid, hRes);
    }
    static HRESULT WINAPI Error(LPCSTR lpszDesc, DWORD dwHelpID, <=== Error
    LPCSTR lpszHelpFile, const IID& iid = GUID_NULL, HRESULT hRes = 0)
    {
    return AtlReportError(GetObjectCLSID(), lpszDesc, dwHelpID,
    lpszHelpFile, iid, hRes);
    } ...
    };--> AtlReportError:
    inline HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCOLESTR lpszDesc,
    const IID& iid = GUID_NULL, HRESULT hRes = 0)
    {
    return AtlSetErrorInfo(clsid, lpszDesc, 0, NULL, iid, hRes, NULL);
    }--> AtlSetErrorInfo:
    ATLINLINE ATLAPI AtlSetErrorInfo(const CLSID& clsid, LPCOLESTR lpszDesc, DWORD dwHelpID,
    LPCOLESTR lpszHelpFile, const IID& iid, HRESULT hRes, HINSTANCE hInst)
    {
    USES_CONVERSION;
    TCHAR szDesc[1024];
    szDesc[0] = NULL;
    // For a valid HRESULT the id should be in the range [0x0200, 0xffff]
    if (IS_INTRESOURCE(lpszDesc)) //id
    {
    UINT nID = LOWORD((DWORD_PTR)lpszDesc);
    ATLASSERT((nID >= 0x0200 && nID <= 0xffff) || hRes != 0);
    if (LoadString(hInst, nID, szDesc, 1024) == 0)
    {
    ATLASSERT(FALSE);
    lstrcpy(szDesc, _T("Unknown Error"));
    }
    lpszDesc = T2OLE(szDesc);
    if (hRes == 0)
    hRes = MAKE_HRESULT(3, FACILITY_ITF, nID);
    } CComPtr<ICreateErrorInfo> pICEI;
    if (SUCCEEDED(CreateErrorInfo(&pICEI)))
    {
    CComPtr<IErrorInfo> pErrorInfo;
    pICEI->SetGUID(iid);
    LPOLESTR lpsz;
    ProgIDFromCLSID(clsid, &lpsz);
    if (lpsz != NULL)
    pICEI->SetSource(lpsz);
    if (dwHelpID != 0 && lpszHelpFile != NULL)
    {
    pICEI->SetHelpContext(dwHelpID);
    pICEI->SetHelpFile(const_cast<LPOLESTR>(lpszHelpFile));
    }
    CoTaskMemFree(lpsz);
    pICEI->SetDescription((LPOLESTR)lpszDesc);
    if (SUCCEEDED(pICEI->QueryInterface(__uuidof(IErrorInfo), (void**)&pErrorInfo)))
    SetErrorInfo(0, pErrorInfo); <====== 抛出COM异常
    }
    return (hRes == 0) ? DISP_E_EXCEPTION : hRes;
    }
      

  15.   

    错误与异常处理--客户端客户端接收组件的错误信息,有两个方式
    1.返回HRESULT2.截获COM异常--- GetErrorInfo()而截获COM异常,也有两个方式:
    2.1 使用 API 方式调用组件HRESULT hr = spXXX->fun() // 调用组件功能
    if( FAILED( hr ) ) // 如果发生了错误
    {
    CComQIPtr < ISupportErrorInfo > spSEI = spXXX; // 组件是否提供了 ISupportErrorInfo 接口?
    if( spSEI ) // 如果支持,那么
    {
    hr = spSEI->InterfaceSupportsErrorInfo( IID_Ixxx ); // 是否支持 Ixxx 接口的错误处理?
    if( SUCCEEDED( hr ) )
    { // 支持,太好了。取出错误信息
    CComQIPtr < IErrorInfo > spErrInfo; // 声明 IErrorInfo 接口
    hr = ::GetErrorInfo( 0, &spErrInfo ); <======== 截获COM异常
    if( SUCCEEDED( hr ) )
    {
    CComBSTR bstrDes;
    spErrInfo->GetDescription( &bstrDes ); // 取得错误描述
    ...... // 还可以取得其它的信息
    2.2 使用 #import 等包装方式调用组件,然后抛出C++异常,再然后由客户端截获
    1.使用 #import 等包装组件
    比如:
    为什么可以使用try/catch结构就可以截获COM异常呢?
    因为,当你使用 #import 引入组件类型库后,编译器帮你包装为一个接口的C++类,而成员函数中,使用了throw,所以你就能catch了。具体它如何包装的,你编译后,打开 tlh 文件去看。如:
    inline _variant_t ISet::GetValue ( _bstr_t Name ) {
        VARIANT _result;
        VariantInit(&_result);
        HRESULT _hr = get_Value(Name, &_result);
        if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this)); <==抛出C++异常
        return _variant_t(_result, false);
    }其中:
    void __stdcall _com_issue_errorex(HRESULT, IUnknown*, REFIID) throw(_com_error);
    _com_issue_errorex的实现MS没有开放源码,但应该就是使用COM异常信息来填充_com_error信息(比如m_hresult, IErrorInfo*等),然后抛出C++异常。*注意:_com_issue_errorex是如何巧妙实现COM异常到C++异常转换的。2. 客户端截获C++异常
    try
    {
    ...... // 调用组件功能
    }
    catch( _com_error &e )
    {
    e.Description(); // 取得错误描述信息
    ...... // 还可以调用 _com_error 函数取得其它信息
    }_com_error的定义如下:
    class _com_error {
    public:
        // Constructors
        //
        _com_error(HRESULT hr,
                   IErrorInfo* perrinfo = NULL,
                   bool fAddRef = false) throw();
        _com_error(const _com_error& that) throw();    // Destructor
        //
        virtual ~_com_error() throw();    // Assignment operator
        //
        _com_error& operator=(const _com_error& that) throw();    // Accessors
        //
        HRESULT Error() const throw();
        WORD WCode() const throw();
        IErrorInfo * ErrorInfo() const throw();    // IErrorInfo method accessors
        //
        _bstr_t Description() const throw(_com_error);
        DWORD HelpContext() const throw();
        _bstr_t HelpFile() const throw(_com_error);
        _bstr_t Source() const throw(_com_error);
        GUID GUID() const throw();    // FormatMessage accessors
        const TCHAR * ErrorMessage() const throw();    // EXCEPINFO.wCode <-> HRESULT mappers
        static HRESULT WCodeToHRESULT(WORD wCode) throw();
        static WORD HRESULTToWCode(HRESULT hr) throw();private:
        enum {
            WCODE_HRESULT_FIRST = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x200),
            WCODE_HRESULT_LAST = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF+1, 0) - 1
        };
        const HRESULT           m_hresult;
        IErrorInfo *            m_perrinfo;
        mutable TCHAR *         m_pszMsg;
    };*注意:其实_com_error就是对IErrorInfo的包装,用截获的COM异常来填充;
      

  16.   

    总之,客户端接收组件错误信息就两种方式
    1。返回HRESULT
    2。组件:SetErrorInfo(..., IErrorInfo *)   抛出COM异常
       客户端:GetErrorInfo(..., IErrorInfo *)   截获COM异常而客户端之所以可以截获C++异常,主要是应为编译时对“返回HRESULT”和GetErrorInfo进行了包装。
      

  17.   

    如果不对返回错误信息进行C++异常包装(_com_error)然后抛出,会很繁的。
    就像这样,处理每个可能的错误,都要一大串代码 ---晕!HRESULT hr = spXXX->fun() // 调用组件功能
    if( FAILED( hr ) ) // 如果发生了错误
    {
    CComQIPtr < ISupportErrorInfo > spSEI = spXXX; // 组件是否提供了 ISupportErrorInfo 接口?
    if( spSEI ) // 如果支持,那么
    {
    hr = spSEI->InterfaceSupportsErrorInfo( IID_Ixxx ); // 是否支持 Ixxx 接口的错误处理?
    if( SUCCEEDED( hr ) )
    { // 支持,太好了。取出错误信息
    CComQIPtr < IErrorInfo > spErrInfo; // 声明 IErrorInfo 接口
    hr = ::GetErrorInfo( 0, &spErrInfo ); <======== 截获COM异常
    if( SUCCEEDED( hr ) )
    {
    CComBSTR bstrDes;
    spErrInfo->GetDescription( &bstrDes ); // 取得错误描述
    ...... // 还可以取得其它的信息还好,微软聪明的工程师帮我们通过C++异常的方式,很巧妙地处理了这个问题。
      

  18.   

    使得程序变得如此简明!^_^try
    {
    spXXX->fun() // 调用组件功能
    }
    catch( _com_error &e )
    {
    e.Description(); // 取得错误描述信息
    ...... // 还可以调用 _com_error 函数取得其它信息
    }
      

  19.   

    对组件错误的处理,根据返回HRESULT,可以获得基本错误信息。
    如果你认为足够了,OK,这就行了。但如果你要向错误信息中加入其它一些信息,这时就需要抛出COM异常---调用Error(...)。
    然后由客户端截获异常,获得额外的信息。
      

  20.   

    总之,客户端接收组件错误信息就两种方式
    1。返回HRESULT
    2。组件:SetErrorInfo(..., IErrorInfo *)   抛出COM异常
       客户端:GetErrorInfo(..., IErrorInfo *)   截获COM异常而组件中使用的Error(....)其实就是对SetErrorInfo的包装
    客户端之所以可以截获C++异常,主要是因为编译时对“返回HRESULT”和GetErrorInfo进行了包装。
    inline _variant_t ISet::GetValue ( _bstr_t Name ) {
        VARIANT _result;
        VariantInit(&_result);
        HRESULT _hr = get_Value(Name, &_result);
        if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this)); <==抛出C++异常
        return _variant_t(_result, false);
    }
      

  21.   

    其实在客户端可以得到的信息就是:HRESULT(_hr)和GetErrorInfo 
    经MS包装后,抛出异常的过程是这样的:
    if (FAILED(_hr){
     使用GetErrorInfo得到的信息,来填充_com_error
     然后抛出 _com_error
    }
      

  22.   

    另外,使用 #import 等包装组件,组件有些方法通过返回值来传递参数,这样就不会返回HRESULT。
    所以,当使用 #import 等包装组件时,必须通过try{}catch{},来捕获错误。
      

  23.   

    另外,使用 #import 等包装组件,组件有些方法通过返回值来传递[out, retval]参数,这样就不会返回HRESULT。
    所以,当使用 #import 等包装组件时,必须通过try{}catch{},来捕获错误。
      

  24.   

    对于支持抛出COM异常的组件,需要ISupportErrorInfo接口
    class ATL_NO_VTABLE CSample : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CParser, &CLSID_Parser>,
    public ISupportErrorInfo,
    public IDispatchImpl<IParser, &IID_IParser, &LIBID_sampleLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
    {
    public:组件的ISupportErrorInfo接口其实就一个方法InterfaceSupportsErrorInfo,用来判断组件中某一接口是否支持截获异常。
    STDMETHODIMP CParser::InterfaceSupportsErrorInfo(REFIID riid)
    {
    static const IID* arr[] = 
    {
    &IID_IParser
    }; for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
    {
    if (InlineIsEqualGUID(*arr[i],riid))
    return S_OK;
    }
    return S_FALSE;
    }也就是说:只有接口位于arr表中时,客户端才可以截获异常。 static const IID* arr[] = 
    {
    &IID_IParser
    };总结:只有接口位于arr表中时,客户端才可以截获异常。但并不影响其在组件实现中抛出异常。为什么要这么作呢?为了效率?
      

  25.   

    继续讨论type_casttype_cast主要作用是在继承树的各类之间作"逆"转换---"由基类转换成派生类".
    而所谓的"正"转换---"由派生类转换成基类",是隐含转换,不需要type_cast.1."由派生类转换成基类"---好理解, 运用虚函数实现多态时,经常用.=================== 虚函数 =====================
    #include <iostream>
    using namespace std;class Base {
    public:
    virtual void fun() {
    cout << "Base::fun" << endl;
    }
    };class Drive : public Base {
    public:
    void fun() {
    cout << "Drive::fun" << endl;
    }
    };int main() {
    Base *pBase;
    Drive *pDrive = new Drive;// 
    pBase = pDrive; <====== "由派生类转换成基类" ---正转换
    pBase->fun(); <====== 调用Drive::funreturn 0;
    }2.而"由基类转换成派生类"的"逆"转换什么时候用到呢?
      -------那就是要实现模版形式的多态时!!=================== 模版 =====================#include <iostream>
    using namespace std;template <typename T>
    class Base {
    public:
    void fun() {
    cout << "Base::fun" << endl;
    }void doSomething() {
    T* pT = static_cast<T*>(this); <== "由基类转换成派生类"的"逆"转换
    pT->fun();
    }
    };class Drive : public Base<Drive> {
    public:
    void fun() {
    cout << "Drive::fun" << endl;
    }
    };int main() {
    Drive obj;
    obj.doSomething();return 0;
    }
      

  26.   

    另外需要注意的是, type_cast对指针地址的影响1.单继承时,不管怎么在类型之间type_cast转换,指针的地址不变.
    2.而对多继承时,类型之间type_cast转换,指针的地址是可能变化的.
    class Drive : public IBase1, public IBase2 {
    }Drive *pDrive = new Drive;// "由派生类转换成基类"
    IBase1 *pIBase1 = pDrive;    // pIBase1 = pDrive
    IBase1 *pIBase2 = pDrive;    // pIBase2 != pDrive == 地址改变//"由基类转换成派生类"的"逆"转换
    pDrive = type_cast<Drive *>pIBase1; //pDrive = pIBase1
    pDrive = type_cast<Drive *>pIBase2;  //pDrive != pIBase2
      

  27.   

    =======================
    static_cast与reinterpret_cast
    static_cast必须是对在具有继承关系的类型之间转换.
    而对reinterpret_cast, 互相转换的类型之间可以没有继承关系.
    就像它名字一样-----对转换后的内容"重新解释".
    有点强制转换的意思!:)对reinterpret_cast, 在任何情况下reinterpret_cast转换,指针的地址不变.pIBase1 = reinterpret_cast<IBase1 *>pIBase2;pIBase1与pIBase2地址一样,但经reinterpret_cast"强制"转换后,pIBase1调用的IBase1的方法.
    ----很危险!那什么情况下会用到reinterpret_cast呢?
      

  28.   

    dynamic_cast主要是对指针或引用进行转换.
    它检查被转换方的数据完整性.
    因此dynamic_cast对从派生类向基类转换总是成功的.比如:
    class CBase { };
    class CDerived: public CBase { };CBase b; CBase* pb;
    CDerived d; CDerived* pd;pb = dynamic_cast<CBase*>(&d);     // ok: derived-to-base
    pd = dynamic_cast<CDerived*>(&b);  // wrong: dynamic_cast不能将基类向派生类转换但dynamic_cast可以对虚类作"基类向派生类转换".但必须runtime检查"被转换方的数据完整性".例如:
    class CBase { virtual void dummy() {} };
    class CDerived: public CBase { int a; };int main () {
      try {
        CBase * pba = new CDerived;
        CBase * pbb = new CBase;
        CDerived * pd;    pd = dynamic_cast<CDerived*>(pba);
        if (pd==0) cout << "Null pointer on first type-cast" << endl;    pd = dynamic_cast<CDerived*>(pbb);
        if (pd==0) cout << "Null pointer on second type-cast" << endl;  } catch (exception& e) {cout << "Exception: " << e.what();}
      return 0;
    }其中:CBase * pba = new CDerived;
    CBase * pbb = new CBase;虽然pba 和pbb都指向CBase *类型.
    但pba 实际指向完整的object of CDerived.
    而pbb指向object of CBase, 非完整的object of CDerived.而dynamic_cast会runtime检查"被转换方的数据完整性".pd = dynamic_cast<CDerived*>(pba); <===== 成功! pba指向完整的object of CDerived.pd = dynamic_cast<CDerived*>(pbb); <===== 失败! pba指向非完整的object of CDerived.
    总结:dynamic_cast就像它名字一样----要runtime检查"被转换方的数据完整性".
      

  29.   

    比较static_cast和dynamic_cast
    static_cast---靠程序员来检查类型转换的完整性
    dynamic_cast---runtime检查类型转换的完整性
      

  30.   

    有了static_cast和dynamic_cast,就可以在类继承树上随意"游动",获得类型之间的转换.
      

  31.   

    现在讨论以下tear off技术
    tear off只要用于当接口很多,容易造成接口"膨胀"的情况.
    比如:
    class CBeachBall :
      public CComObjectRootEx<CBeachBall>,
      public ISphere,
      public IRollableObject,
      public IPlaything,
      public ILethalObject, <====不经常用
      public ITakeUpSpace,
      public IWishIWereMoreUseful,
      public ITryToBeHelpful,
      public IAmDepressed {...};CBeachBall一共实现了8个接口,这样每个组件实例都要由8个vptr,需要额外32个字节的开销.
    而这8个接口并不是每个都很常用.比如ILethalObject就不经常,但还占用每个组件实例的4个字节的空间---浪费!!怎么办? 使用tear off技术来解决!不是ILethalObject不常用吗, 那就将其从接口列表中去掉!当真有ILethalObject接口请求时,动态生成
    实现ILethalObject接口的实例,然后调用这个实例中的实现.如下:
    CBeachBallLethalness实现了ILethalObject接口class CBeachBallLethalness :
      public CComTearOffObjectBase<CBeachBall,
        CComSingleThreadModel>,
      public ILethalObject {
    public:
    BEGIN_COM_MAP(CBeachBallLethalness)
      COM_INTERFACE_ENTRY(ILethalObject)
    END_COM_MAP()  // ILethalObject methods
      STDMETHODIMP Kill() {
        m_pOwner->m_gasFill = GAS_HYDROGEN;
        m_pOwner->HoldNearOpenFlame();
        return S_OK;
      }
    };去掉ILethalObject接口, CBeachBall变成: 
    class CBeachBall :
      public CComObjectRootEx<CBeachBall>,
      public ISphere,
      public IRollableObject,
      public IPlaything,
      //public ILethalObject,  // 去掉ILethalObject接口
      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) // 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; // CBeachBallLethalness作为友元加入
    };
    总结:tear off技术就是要去掉组件中--"不常用接口",减轻每个实例的内存开销.
    只有当真正使用该接口时,在动态生成"实现该接口的组件",实现调用.
      

  32.   

    这些天看COM/ATL的书,感觉到处都写着接口.
    不管是COM异常处理,集合/枚举,还是连接点,处处都体现接口的光芒.如果你觉得某些实现必须要传递某个结构或一个数组---且慢!一定是接口设计没有到位!
    接口粒度还不够小!1.传递结构数据的话,可以参考COM处理的实现
    ---用SetErrorInfo(,*IErrorInfo)和GetErrorInfo(,*IErrorInfo),来抛出或截获COM异常.
    其实它也在作"传递有关异常的结构数据",但它采用的接口的方式.避免结构数据的传递.
    最终结构数据的传递,是通过接口IErrorInfo中的一些get/set实现的.所以说,组件编程,要换脑子,避免以前养成的编程定势,处处要以接口方式思考问题.
    可以将组件想象成一个芯片----只想怎样通过管脚来实现功能,而不是奢望打开芯片取数据---那是不可能的!!总结:组件编程,是不同于以往OP,OO编程的新的编程方式.-----一切都是接口!
      

  33.   

    采访一下,你看什么书学的,how much
      

  34.   

    wishfly以不能发帖,换个马甲:)
    深入解析ATL -- ¥69 -- 好像现在没卖的了