1 关于句柄类
  句柄类也完全可以实现:接口和实现的分离,Don Box在书中也讲到这一点,但是vc开发com组件好像没有采用句柄类的,像MFC采用的嵌套类技术,ATL采用的抽象基类的多继承,模版技术,为什么句柄类没有得到应用???谁能给解释解释2关于嵌套类
com的本质是一个接口与实现分离的C++类,如何实现接口和实现的分离是一个问题,通过用纯虚类做为基类来实现接口和实现的分离,我可以理解,但是嵌套类怎么能实现接口和实现的分离,那位老大能给解释一下??
 纯虚类可以作为接口,是因为纯虚类不包含数据成员,只是通过虚函数表可以提供接口函数的入口地址
但是嵌套类就不同了,它有自己的数据成员,比如CCmdTarget类,ccmdtarget是如何来体现二进制的接口协议的呢?欢迎高手参加讨论,如果你们能将嵌套类实现com二进制协议接口讲述的很清楚的话,我开贴另谢

解决方案 »

  1.   

    贴出CCmdTarget类的详细定义,供讨论#ifdef _AFXDLL
    class CCmdTarget : public CObject
    #else
    class AFX_NOVTABLE CCmdTarget : public CObject
    #endif
    {
    DECLARE_DYNAMIC(CCmdTarget)
    protected:public:
    // Constructors
    CCmdTarget();// Attributes
    LPDISPATCH GetIDispatch(BOOL bAddRef);
    // retrieve IDispatch part of CCmdTarget
    static CCmdTarget* PASCAL FromIDispatch(LPDISPATCH lpDispatch);
    // map LPDISPATCH back to CCmdTarget* (inverse of GetIDispatch)
    BOOL IsResultExpected();
    // returns TRUE if automation function should return a value// Operations
    void EnableAutomation();
    // call in constructor to wire up IDispatch
    void EnableConnections();
    // call in constructor to wire up IConnectionPointContainer void BeginWaitCursor();
    void EndWaitCursor();
    void RestoreWaitCursor();       // call after messagebox#ifndef _AFX_NO_OLE_SUPPORT
    // dispatch OLE verbs through the message map
    BOOL EnumOleVerbs(LPENUMOLEVERB* ppenumOleVerb);
    BOOL DoOleVerb(LONG iVerb, LPMSG lpMsg, HWND hWndParent, LPCRECT lpRect);
    #endif// Overridables
    // route and dispatch standard command message types
    //   (more sophisticated than OnCommand)
    virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,
    AFX_CMDHANDLERINFO* pHandlerInfo);#ifndef _AFX_NO_OLE_SUPPORT
    // called when last OLE reference is released
    virtual void OnFinalRelease();
    #endif#ifndef _AFX_NO_OLE_SUPPORT
    // called before dispatching to an automation handler function
    virtual BOOL IsInvokeAllowed(DISPID dispid);
    #endif#ifndef _AFX_NO_OLE_SUPPORT
    // support for OLE type libraries
    void EnableTypeLib();
    HRESULT GetTypeInfoOfGuid(LCID lcid, const GUID& guid,
    LPTYPEINFO* ppTypeInfo);
    virtual BOOL GetDispatchIID(IID* pIID);
    virtual UINT GetTypeInfoCount();
    virtual CTypeLibCache* GetTypeLibCache();
    virtual HRESULT GetTypeLib(LCID lcid, LPTYPELIB* ppTypeLib);
    #endif// Implementation
    public:
    virtual ~CCmdTarget();
    #ifdef _DEBUG
    virtual void Dump(CDumpContext& dc) const;
    virtual void AssertValid() const;
    #endif
    #ifndef _AFX_NO_OLE_SUPPORT
    void GetNotSupported();
    void SetNotSupported();
    #endifprotected:
    friend class CView; CView* GetRoutingView();
    CFrameWnd* GetRoutingFrame();
    static CView* PASCAL GetRoutingView_();
    static CFrameWnd* PASCAL GetRoutingFrame_();
    DECLARE_MESSAGE_MAP()       // base class - no {{ }} macros#ifndef _AFX_NO_DOCOBJECT_SUPPORT
    DECLARE_OLECMD_MAP()
    friend class COleCmdUI;
    #endif#ifndef _AFX_NO_OLE_SUPPORT
    DECLARE_DISPATCH_MAP()
    DECLARE_CONNECTION_MAP()
    DECLARE_INTERFACE_MAP()#ifndef _AFX_NO_OCC_SUPPORT
    DECLARE_EVENTSINK_MAP()
    #endif // !_AFX_NO_OCC_SUPPORT // OLE interface map implementation
    public:
    // data used when CCmdTarget is made OLE aware
    long m_dwRef;
    LPUNKNOWN m_pOuterUnknown;  // external controlling unknown if != NULL
    DWORD m_xInnerUnknown;  // place-holder for inner controlling unknownpublic:
    // advanced operations
    void EnableAggregation();       // call to enable aggregation
    void ExternalDisconnect();      // forcibly disconnect
    LPUNKNOWN GetControllingUnknown();
    // get controlling IUnknown for aggregate creation // these versions do not delegate to m_pOuterUnknown
    DWORD InternalQueryInterface(const void*, LPVOID* ppvObj);
    DWORD InternalAddRef();
    DWORD InternalRelease();
    // these versions delegate to m_pOuterUnknown
    DWORD ExternalQueryInterface(const void*, LPVOID* ppvObj);
    DWORD ExternalAddRef();
    DWORD ExternalRelease(); // implementation helpers
    LPUNKNOWN GetInterface(const void*);
    LPUNKNOWN QueryAggregates(const void*); // advanced overrideables for implementation
    virtual BOOL OnCreateAggregates();
    virtual LPUNKNOWN GetInterfaceHook(const void*); // OLE automation implementation
    protected:
    struct XDispatch
    {
    DWORD m_vtbl;   // place-holder for IDispatch vtable
    #ifndef _AFX_NO_NESTED_DERIVATION
    size_t m_nOffset;
    #endif
    } m_xDispatch;
    BOOL m_bResultExpected; // member variable-based properties
    void GetStandardProp(const AFX_DISPMAP_ENTRY* pEntry,
    VARIANT* pvarResult, UINT* puArgErr);
    SCODE SetStandardProp(const AFX_DISPMAP_ENTRY* pEntry,
    DISPPARAMS* pDispParams, UINT* puArgErr); // DISPID to dispatch map lookup
    static UINT PASCAL GetEntryCount(const AFX_DISPMAP* pDispMap);
    const AFX_DISPMAP_ENTRY* PASCAL GetDispEntry(LONG memid);
    static LONG PASCAL MemberIDFromName(const AFX_DISPMAP* pDispMap, LPCTSTR lpszName); // helpers for member function calling implementation
    static UINT PASCAL GetStackSize(const BYTE* pbParams, VARTYPE vtResult);
    #ifdef _PPC_
    SCODE PushStackArgs(BYTE* pStack, const BYTE* pbParams,
    void* pResult, VARTYPE vtResult, DISPPARAMS* pDispParams,
    UINT* puArgErr, VARIANT* rgTempVars, UINT nSizeArgs);
    #else
    SCODE PushStackArgs(BYTE* pStack, const BYTE* pbParams,
    void* pResult, VARTYPE vtResult, DISPPARAMS* pDispParams,
    UINT* puArgErr, VARIANT* rgTempVars);
    #endif
    SCODE CallMemberFunc(const AFX_DISPMAP_ENTRY* pEntry, WORD wFlags,
    VARIANT* pvarResult, DISPPARAMS* pDispParams, UINT* puArgErr); friend class COleDispatchImpl;#ifndef _AFX_NO_OCC_SUPPORT
    public:
    // OLE event sink implementation
    BOOL OnEvent(UINT idCtrl, AFX_EVENT* pEvent,
    AFX_CMDHANDLERINFO* pHandlerInfo);
    protected:
    const AFX_EVENTSINKMAP_ENTRY* PASCAL GetEventSinkEntry(UINT idCtrl,
    AFX_EVENT* pEvent);
    #endif // !_AFX_NO_OCC_SUPPORT // OLE connection implementation
    struct XConnPtContainer
    {
    DWORD m_vtbl;   // place-holder for IConnectionPointContainer vtable
    #ifndef _AFX_NO_NESTED_DERIVATION
    size_t m_nOffset;
    #endif
    } m_xConnPtContainer;#ifdef _AFXDLL
    AFX_MODULE_STATE* m_pModuleState;
    friend class CInnerUnknown;
    friend UINT APIENTRY _AfxThreadEntry(void* pParam);
    #endif virtual BOOL GetExtraConnectionPoints(CPtrArray* pConnPoints);
    virtual LPCONNECTIONPOINT GetConnectionHook(const IID& iid); friend class COleConnPtContainer;#endif //!_AFX_NO_OLE_SUPPORT
    };
      

  2.   

    怎么没有人回答啊,自己顶一下提出第三个问题
    3包装类
     我们知道在调用com组件的时候,一般都有三种方法,其中一种就是通过com组件的tlb库生成包装类,谁能给解释一下,为什么要通过包装类调用com?why
      

  3.   

    你学多少了,你是用mfc写的,还是直接用atl写的
      

  4.   

    我也刚学,瞎说点自己的看法com和mfc写法相比就是多了个壳子,这个壳子(应该就是你说的包装类)支持其他语言的调用,实现2进制级的代码重用问题1太深刻了,俺不知道,第2个我觉得它的变量和控件的属性差不多,主要是自己用,别人想要用通过函数接口来访问,我也想听权威的解释,只是用用感觉很不爽,总想弄明白这些问题
      

  5.   

    1.句柄类确实可以基本做到把对象的行为和实现相分离(还不够),但是远谈不上做到了COM的实际要求,首先句柄类本身就存在实现,它要负责转发接口函数的请求给实际的实现类,这就要求它要了解实现类的细节,所以说,句柄类只是对客户隐藏了对象的实现,但它较接口来讲又与对象的实现类过于耦合。另外,使用句柄类在每一次方法调用至少要多执行一次方法调用(即本身的调用转发函数),再者,应用起来也太麻烦,需要为每个接口函数写转发实现。2.嵌套类的方法是指把多个实现单一或一组简单接口的实现类包装成一个类,由于这个类内部的类实现了接口,所以它本身并不从接口继承,也不存在接口函数实现。当然基本的IUnkown方法还是实现了,但仅仅是起到向内部实际的接口实现类做转发,由于各个内部实现类各自实现自己的接口方法,所以不存在接口方法名冲突的问题(多继承时会出现同名方法实现相互覆盖的问题),这在对已有复杂类库进行COM化时大有好处,所以MFC的COM实现使用了这种方法。具体原理很简单,《COM本质论》里有介绍,不多说了。
      

  6.   

    欢迎楼上的继续讨论下面的一句话
    "包含虚拟函数的C++对象都将包含一个指向虚函数表的指针,这种结构符合COM接口规范。"
    这句话怎么理解
    可以理解为:所有具有虚函数的类的声明,都可以作为com组件的接口,这么理解对吗?
    还是只有具有纯虚函数的抽象基类才可以作为com的接口?我一直认为,只有抽象基类可以作为接口,因为抽象基类没法声明对象,只能通过派生类来声明对象,抽象基类只是提供了接口函数的入口地址,ATL一般都是以抽象基类作为接口的,然后通过多继承实现多接口,但是MFC是从CCmdTarget类继承过来的,我看了ccmdtarget类的定义了,它不是抽象基类,那它是如何实现接口的?当然ccmdtarget是通过嵌套类对象和父类指针的偏移表来实现多接口的,这个我可以理解,我现在不明白的就是从ccmdtartget派生出来的类怎么可以实现二进制接口了?它又不是抽象基类,如果ccmdtarget添加了数据成员了,它的对象的大小肯定会发生变化,请继续讨论
      

  7.   

    接口都是纯虚类,它所包含的函数都是纯虚函数,而且它没有成员变量。而COM组件就是从这些纯虚类继承下来的派生类,它实现了这些虚函数,仅此而已..[引用]
    俺也是才学习COM.大致和楼上的理解差不多...
    观注.......
      

  8.   

    ccmdtarget当然不是抽象基类,确切的说,它是接口实现类的外套类,同时也是一个接口实现类。
    从接口继承的类是接口实现类,再从这个类继承的类也是实现类,同样嵌套了该类的类也是实现类。
    class A:public IXXX {...}
    class B:public A {...}
    class C {
        ...
        class C1 : public IXXX {...}
        ...
    }
    以上A、B、C都是接口实现类(COM对象),因为它们都有一个共同的特点,就是其类实例都拥有一个特殊的成员变量-IXXX的vptr和都拥有一个虚函数表。
    C就是使用嵌套类的情况,ccmdtarget就是这种类。
      

  9.   

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmfc98/html/_mfcnotes_tn038.asp
      

  10.   

    谢谢麦猪的精彩发言,昨天晚上回去重读了一下don box的《com本质论》已经基本能明白了,大师的书总是要读3遍才能理解其精髓的
    本来早上来想结贴的,看楼上还有很多人不明白,希望继续讨论,同时也给 小星星,和月吻长河推荐com本质论,这本书,里面对com讲的很透彻,我这里有电子版,如果需要,请联系我,希望麦猪接口的内存表示形式给大家讲一下,如果这个周末没有事情的话,我会将我对com的理解写出来给大家贴出来,下周结贴
      

  11.   

    <<COM本质论>>的确不错,小潘译的,昨天DOWN回去看了开头的章节...感觉就像一个节目的口号"原理是什么,这夜我们说清楚!!".
    呵呵...正在学习..希望大家多交流一下心得.
      

  12.   

    [引用]:
    (1) COM组件实际上是一个C++类,而接口都是纯虚类。组件从接口派生而来。我们可以简单的用纯粹的C++的语法形式来描述COM是个什么东西: 
      class IObject
      {
      public:
        virtual Function1(...) = 0;
        virtual Function2(...) = 0;
        ....
      };
      class MyObject : public IObject
      {
      public:
        virtual Function1(...){...}
        virtual Function2(...){...}
    ....
      }; 
      看清楚了吗?IObject就是我们常说的接口,MyObject就是所谓的COM组件。切记切记接口都是纯虚类,它所包含的函数都是纯虚函数,而且它没有成员变量。而COM组件就是从这些纯虚类继承下来的派生类,它实现了这些虚函数,仅此而已。从上面也可以看出,COM组件是以 C++为基础的,特别重要的是虚函数和多态性的概念,COM中所有函数都是虚函数,都必须通过虚函数表VTable来调用,这一点是无比重要的,必需时刻牢记在心。
      

  13.   

    to:phoenixsharp(小星星)
    嘿嘿~~
    COM真的有这么简单就好了。
    个人觉得COM为了跨越语言界限付出了很重的代价,加上跨进程、线程间对象
    引用让人觉得学习COM只有“难”一个字。