vc编写com,谁能指导指导方向!!谢谢

解决方案 »

  1.   

    Introduction
    As part of a project I'm working on I need to implement multiple COM interfaces. 
    If I wanted to implement the IWidget interface and the IThingAMaBob interface it might look like this.class CWidgets : public IWidgets
    {
    public:
        virtual HRESULT __stdcall QueryInterface(REFIID riid, 
                                         void __RPC_FAR *__RPC_FAR *ppvObject);
        virtual ULONG   __stdcall AddRef(void);
        virtual ULONG   __stdcall Release(void);    virtual HRESULT __stdcall MyExtraFunction();
    };and similarly for the IThingAMaBob interface. This is COM 101 material. 
    It becomes a bit tedious coding this all the time. Factor in the need to implement, for each interface, the IUnknown base methods correctly (especially QueryInterface) and the smart programmer starts researching better (easier) ways to do it.Fortunately, for the MFC programmer, MFC provides a much easier way to not only implement a COM interface, it provides an easy way to define multple interfaces in the one class.MFC COM Macros
    We can rewrite our CWidgets class like this, using some MFC macros. class CWidgets : public CCmdTarget
    {
    public:
        DECLARE_INTERFACE_MAP()    BEGIN_INTERFACE_PART(Widgets, IWidgets)
            STDMETHOD(MyExtraFunction)();
        END_INTERFACE_PART(Widgets)
    };The first thing you'll notice is that the class is now derived from CCmdTarget. It needs to be derived from CCmdTarget either directly or indirectly for reasons we'll see a little later. The class then declares an interface map followed by an interface part. The interface part defines a nested class called (in this case) XWidgets which is derived from the IWidgets interface. (The macro prepends an X to the first parameter of the macro to give the nested class a unique name that won't clash with the 'C' or 'I' names). The BEGIN_INTERFACE_PART macro also declares the 3 standard methods inherited from IUnknown for us. Any additional methods for this interface must be defined between the BEGIN_INTERFACE_PART and END_INTERFACE_PART macros. 
    In our implementation file we'd have something like this.BEGIN_INTERFACE_MAP(CWidgets, CCmdTarget)
        INTERFACE_PART(CWidgets, IID_IWidgets, Widgets)
    END_INTERFACE_MAP()plus our implementation of the methods of the IWidgets interface. Note that even though we don't have to declare the basic IUnknown methods we do have to implement them. The four methods of our IWidgets interface might look like this. STDMETHODIMP_(ULONG) CWidgets::XWidgets::AddRef()
    {
        METHOD_PROLOGUE(CWidgets, Widgets);
        
        return pThis->ExternalAddRef();
    }STDMETHODIMP_(ULONG) CWidgets::XWidgets::Release()
    {
        METHOD_PROLOGUE(CWidgets, Widgets)    return pThis->ExternalRelease();
    }STDMETHODIMP CWidgets::XWidgets::QueryInterface(REFIID iid, LPVOID far* ppvObj)     
    {
        METHOD_PROLOGUE(CWidgets, Widgets)    return pThis->ExternalQueryInterface(&iid, ppvObj);
    }STDMETHODIMP CWidgets::XWidgets::MyExtraFunction()
    {
        METHOD_PROLOGUE(CWidgets, Widgets)    //  Do something
        .
        .
        .
        
        return S_OK;
    }Before we dive into this let's recap. Our class is derived from CCmdTarget and, via the BEGIN_INTERFACE_PART macro, contains a nested class called XWidgets. 
    The first thing you'll notice in the nested class implementation is a new macro, METHOD_PROLOGUE. It takes two arguments. The first is the name of the enclosing class, the second is the name of the inner class (without the X). If we look at the definition of the macro we see this#define METHOD_PROLOGUE(theClass, localClass) \
        theClass* pThis = \
            ((theClass*)((BYTE*)this - offsetof(theClass, m_x##localClass))); \
        AFX_MANAGE_STATE(pThis->m_pModuleState) \
        pThis; // avoid warning from compiler \which the preprocessor replaces with     CWidgets* pThis = \
            ((CWidgets*)((BYTE*)this - offsetof(CWidgets, m_xWidgets:))); \
        AFX_MANAGE_STATE(pThis->m_pModuleState) \
        pThis; // avoid warning from compiler \.
      

  2.   

    which declares a pointer called pThis of type CWidgets and initialises it to point at the enclosing CWidgets class. It does this via some pointer arithmetic using the this pointer and m_xWidgets. Ignore, for now, how m_xWidgets got there, simply accept that it's an embedded instance of the XWidgets class.Once we've got a pointer to the enclosing class we can access data in the enclosing class or call member functions on the enclosing class. This is where the derivation from CCmdTarget comes into play.MFC provides us with an implementation of AddRef() and Release() which we can access via pThis. Hence the calls to ExternalAddRef() etc. ExternalAddRef() either calls our external IUnknown if our interface is aggregrated, or it increments a reference counter in our base CCmdTarget object. Since I'm not concerned in this article with aggregration that's the last mention you'll see of the subject. Similarly, calling ExternalRelease() decrements our reference count.More importantly, MFC gives us a correct implementation of QueryInterface(). By correct I mean that it follows the semantics of QueryInterface. If you have an IWidgets interface you can use it to obtain an IUnknown interface. Or vice versa. Which is, of course, the way it's supposed to work but a surprisingly high number of programmers (myself included) sometimes forget to implement the query for IUnknown.Instances of nested classes
    Once we've defined our interface and written the implementation we need to have an instance of the class to use. One might be tempted to do this in our outer class. class CWidgets : public CCmdTarget
    {
    public:
        DECLARE_INTERFACE_MAP()    BEGIN_INTERFACE_PART(Widgets, IWidgets)
            STDMETHOD(MyExtraFunction)();
        END_INTERFACE_PART(Widgets)
        
        XWidgets    m_myWidget;
    };which defines the XWidgets class and then creates an instance of it embedded in the CWidgets object. That'll certainly compile but when your client code calls QueryInterface() to get an instance of the XWidgets class it won't get a pointer to m_myWidget. What it gets is a pointer to an instance of XWidgets called m_xWidgets. Hang on! Where did m_xWidgets come from?Hidden inside the END_INTERFACE_PART macro is a declaration of an instance of the nested class. Note this well. Usually when you define a nested class you have to declare an instance of the nested class within the enclosing class. The MFC macros assume that you'll always have an embedded instance of the nested class in the enclosing object, so it creates one for you, naming the instance by prepending m_x to the nested class name.Steak knives anyone?
    So far so good. But wait, there's more! The macros also make it very easy to implement multiple interfaces in the one object. Let's expand our class a little to incorporate an implementation of the IThingAMaBob interface. Of course, it only makes sense to combine the two interfaces in the one object if a Widget and a ThingAMaBob are related so let's assume they are. Our class would now look like this.class CWidgets : public CCmdTarget
    {
    public:
        DECLARE_INTERFACE_MAP()    BEGIN_INTERFACE_PART(Widgets, IWidgets)
            STDMETHOD(MyExtraFunction)();
        END_INTERFACE_PART(Widgets)
        
        BEGIN_INTERFACE_PART(ThingAMaBob, IThingAMaBob)
            STDMETHOD(SomeOtherFunction)();
        END_INTERFACE_PART(ThingAMaBob)
    };which defines a second nested class called XThingAMaBob. In our implementation file the interface map now looks like this. BEGIN_INTERFACE_MAP(CWidgets, CCmdTarget)
        INTERFACE_PART(CWidgets, IID_IWidgets, Widgets)
        INTERFACE_PART(CWidgets, IID_IThingAMaBob, ThingAMaBob)
    END_INTERFACE_MAP()and we'd add whatever methods are part of the XThingAMaBob class (not forgetting the IUnknown methods). As part of all this we get a new member variable in the containing class called m_xThingAMaBob of type XThingAMaBob. 
    The really nice thing about this is that if QueryInterface() in your implementation calls pThis->ExternalQueryInterface() you'll automatically find and return the embedded m_xSomething instance associated with the iid you (or some other program) requested, with all the plumbing taken care of by the interface map and CCmdTarget. You don't have to write a bunch of code in each objects QueryInterface() method to make it aware of the other interfaces implemented on the containing object. More importantly, if you add an interface to the class, all you have to remember is to add it to the interface map in the implementation file and MFC will take care of the rest.Reference Counting
    Because the interfaces are defined within an MFC object they have the same lifetime as the enclosing object. Thus, as you've already seen, it's possible to do reference counting in the enclosing object rather than make each interface responsible for it's own counting. All interfaces implemented on the class share a single reference counter. In debug builds MFC does an ASSERT(dwRef <= 1) on the reference counter during the CCmdTarget::~CCmdTarget() destructor. If you encounter that assert it means that someone somewhere hasn't done a Release() on one or more of your interface pointers. 
    Variables, methods and constructors in the nested class
    As well as defining COM methods for a nested class it's possible to embed normal (non COM) methods, constructors and destructors and member variables inside a nested class. All you have to do is add them between the BEGIN_INTERFACE_PART and END_INTERFACE_PART macros. The only 'special' thing you have to remember is that the constructor / destructor names are the classname with an 'X' prepended. class CWidgets : public CCmdTarget
    {
    public:
        DECLARE_INTERFACE_MAP()    BEGIN_INTERFACE_PART(Widgets, IWidgets)
            STDMETHOD(MyExtraFunction)();
        END_INTERFACE_PART(Widgets)
        
        BEGIN_INTERFACE_PART(ThingAMaBob, IThingAMaBob)
            STDMETHOD(SomeOtherFunction)();
            
            XThingAMaBob();
            ~XThingAMaBob();
            
        private:
            BOOL    m_bMyBool;
        END_INTERFACE_PART(ThingAMaBob)
    };Which defines a constructor / destructor pair called XThingAMaBob and a private BOOL variable called m_bMyBool. Within your nested class code you access members defined this way just as you would in a non nested class, via the implicit this pointer
      

  3.   

    正在写一个相关的COM开发的文章,还没有完成。
    用ATL开发COM,里面可以用MFC的东西。`
      

  4.   

    还是可以的,但一般用atl的时候多一些。
      

  5.   

    当然可以,但不如atl来的简单些。因为atl设计就是用来开始com或活动对象的。实际上你用纯C++也是没任何问题的。但要注意,没必要既用atl又用mfc,因为我感觉在vc.net2005中的atl提供了所有mfc提供的功能,已经不像以前的atl版本了。如果两个你都用的话,没必要,而且同时你在部署你的软件时会发布比较少一些的rtl文件。
      

  6.   

    com 的实质就是一些DLL等,c++的纯虚函数!必实现一些特定的纯虚函数???
        不知道我这样理解这一问题是不是准确,错在那里??
    还有就是 com中编程特别注意的地方是什么!(我是用mfc的)
      

  7.   

    com 的实质就是一些DLL等,c++的纯虚函数!必实现一些特定的纯虚函数???
        不知道我这样理解这一问题是不是准确,错在那里??
    ---
    当你深入学习后应该会站在另外一个层次看com了...
      

  8.   

    当你深入学习后应该会站在另外一个层次看com了...因为没有深入才有发问,想有一个整体的印象,少走一些弯路,
    所以请赐教!!
      

  9.   

    用mfc,当然可以,我感觉mfc和atl的关系,就象是不用stl的C++和stl的关系