| Tenouk C & C++ | MFC Home | COM Part 1 | COM Part 3 | Download | Site Index |


 

 

 

 

 

The Component Object Model - COM Part 2

 

 

 

 

 

 

 

Program examples compiled using Visual C++ 6.0 compiler on Windows XP Pro machine with Service Pack 2. Topics and sub topics for this tutorial are listed below. Don’t forget to read Tenouk’s small disclaimer. The supplementary notes for this tutorial are OLE, mfc, COleObjectFactory class and .NET.

  1. The MYMFC28A Example: A Simulated COM

  2. The MYMFC28A From Scratch

 

 

The MYMFC28A Example: A Simulated COM

 

Listings 1, 2, 3, and 4 show code for a working "simulated COM" program, MYMFC28A. This is a Win32 Console Application (without the MFC library) that uses a class factory to construct an object of class CSpaceship, calls its interface functions, and then releases the spaceship. The Interface.h header file, shown in Listing 1, contains the CSimulatedCmdTarget base class and the interface declarations that are used by both the client and component programs. The Spaceship.h header file shown in Listing 2 contains the spaceship-specific class declarations that are used in the component program. Spaceship.cpp, shown in Listing 3, is the component that implements GetClassObject(); Client.cpp, shown in Listing 4, is the client that calls GetClassObject(). What's phony here is that both client and component code are linked within the same mymfc28A.exe program. Thus, our simulated COM is not required to make the connection at runtime. You'll see how that's done later in this module.

 

The MYMFC28A From Scratch

 

The following are the steps to build MYMFC28A project.

 

MYMFC28A COM project-AppWizard new project.

 

Figure 4: MYMFC28A COM project-AppWizard new project.

 

MYMFC28A COM project-AppWizard step 1 of 1.

 

Figure 5: MYMFC28A COM project-AppWizard step 1 of 1.

 

MYMFC28A COM project summary.

 

Figure 6: MYMFC28A COM project summary.

 

Now we are going to add new header and source files to the project.

 

Figure 7: Adding new files to project.

 

Figure 7: Adding new files to project.

 

Figure 8: Adding new header file to project.

 

Figure 8: Adding new header file to project.

 

Then copy and paste the Interface.h code given in the listing. Repeat the same step for other files: Spaceship.h, Spaceship.cpp and Client.cpp.

 

Build and run the program.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Figure 9: Building Visual C++ project.

 

Figure 9: Building Visual C++ project.

 

Figure 10: Running Visual C++ project.

 

Figure 10: Running Visual C++ project.

 

The following is the MYMFC28A output screen, depicting the program activities.

 

Entering CSimulatedCmdTarget ctor at 00427930

Entering IUnknown ctor at 00427938

Entering IClassFactory ctor at 00427938

Entering XClassFactory ctor at 00427938

Entering CSpaceshipFactory ctor at 00427930

 

Entering client main()

 

Entering CSpaceshipFactory::ExternalQueryInterface()--nIid =  1

 

Entering CSpaceshipFactory::XClassFactory::CreateInstance()

Entering CSimulatedCmdTarget ctor at 00430060

Entering IUnknown ctor at 00430074

Entering IMotion ctor at 00430074

Entering XMotion ctor at 00430074

Entering IUnknown ctor at 00430078

Entering IVisual ctor at 00430078

Entering XVisual at ctor

Entering CSpaceship ctor at 00430060

 

Entering CSpaceship::ExternalQueryInterface()--nIid = 0

Entering CSimulatedCmdTarget::ExternalRelease()--RefCount = 2

 

Entering CSpaceship::XMotion::QueryInterface()--nIid =  2

 

Entering CSpaceship::ExternalQueryInterface()--nIid = 2

 

Entering CSpaceship::XMotion::QueryInterface()--nIid =  3

 

Entering CSpaceship::ExternalQueryInterface()--nIid = 3

 

main(): pUnk = 00430074, pMot = 00430074, pDis = 00430078

 

 

Entering CSpaceship::XMotion::Fly()

this pointer = 00430074, pThis pointer = 00430060

m_nPosition = 100

m_nAcceleration = 101

 

Entering CSpaceship::XMotion::GetPosition()

this pointer = 00430074, pThis pointer = 00430060

m_nPosition = 100

m_nAcceleration = 101

nPos = 100

 

Entering CSpaceship::XVisual::Display()

this pointer = 00430078, pThis pointer = 00430060

m_nPosition = 100

m_nColor = 102

 

Entering CSpaceshipFactory::XClassFactory::Release()

Entering CSimulatedCmdTarget::ExternalRelease()--RefCount = 2

 

Entering CSpaceship::XMotion::Release()

Entering CSimulatedCmdTarget::ExternalRelease()--RefCount = 3

 

Entering CSpaceship::XMotion::Release()

Entering CSimulatedCmdTarget::ExternalRelease()--RefCount = 2

 

Entering CSpaceship::XVisual::Release()

Entering CSimulatedCmdTarget::ExternalRelease()--RefCount = 1

deleting

Entering CSpaceship dtor at 00430060

Entering CSimulatedCmdTarget dtor at 00430060

Entering CSpaceshipFactory dtor at 00427930

Entering CSimulatedCmdTarget dtor at 00427930

Press any key to continue

 

 

INTERFACE.H

 

// definitions that make our code look like MFC code

#define BOOL   int

#define DWORD  unsigned int

#define TRUE   1

#define FALSE  0

#define TRACE  printf

#define ASSERT assert

 

//----------definitions and macros-----------------------------------

#define CLSID_CSpaceship    10

#define IID_IUnknown        0

#define IID_IClassFactory   1

#define IID_IMotion         2

#define IID_IVisual         3

 

// this macro for 16-bit Windows only

#define METHOD_PROLOGUE(theClass, localClass) \

    theClass* pThis = ((theClass*)((char*)(this) - \

    offsetof(theClass, m_x##localClass))); \

 

 

BOOL GetClassObject(int nClsid, int nIid, void** ppvObj);

 

//----------interface declarations-----------------------------------

struct IUnknown

{

    IUnknown() { TRACE("Entering IUnknown ctor at %p\n", this); }

    virtual BOOL QueryInterface(int nIid, void** ppvObj) = 0;

    virtual DWORD Release() = 0;

    virtual DWORD AddRef() = 0;

};

 

struct IClassFactory : public IUnknown

{

    IClassFactory()

        { TRACE("Entering IClassFactory ctor at %p\n", this); }

    virtual BOOL CreateInstance(int nIid, void** ppvObj) = 0;

};

 

struct IMotion : public IUnknown

{

    IMotion() { TRACE("Entering IMotion ctor at %p\n", this); }

    virtual void Fly() = 0; // pure

    virtual int& GetPosition() = 0;

};

 

struct IVisual : public IUnknown

{

    IVisual() { TRACE("Entering IVisual ctor at %p\n", this); }

    virtual void Display() = 0;

};

 

class CSimulatedCmdTarget // 'simulated' CSimulatedCmdTarget

{

public:

    DWORD m_dwRef;

 

protected:

    CSimulatedCmdTarget()

       {

        TRACE("Entering CSimulatedCmdTarget ctor at %p\n", this);

        m_dwRef = 1; // implied first AddRef

    }

    virtual ~CSimulatedCmdTarget()

        { TRACE("Entering CSimulatedCmdTarget dtor at %p\n", this); }

    DWORD ExternalRelease()

       {

    TRACE("Entering CSimulatedCmdTarget::ExternalRelease()--RefCount = %ld\n", m_dwRef);

        if (m_dwRef == 0)

            return 0;

        if(--m_dwRef == 0L)

              {

            TRACE("deleting\n");

            delete this;

            return 0;

        }

        return m_dwRef;

    }

    DWORD ExternalAddRef() { return ++m_dwRef; }

};

 

 

Listing 1: The Interface.h file.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

SPACESHIP.H

 

class CSpaceship;

 

//----------class declarations-----------------------------------------

class CSpaceshipFactory : public CSimulatedCmdTarget

{

public:

    CSpaceshipFactory()

        { TRACE("Entering CSpaceshipFactory ctor at %p\n", this); }

    ~CSpaceshipFactory()

        { TRACE("Entering CSpaceshipFactory dtor at %p\n", this); }

    BOOL ExternalQueryInterface(int lRid, void** ppvObj);

    class XClassFactory : public IClassFactory

    {

    public:

        XClassFactory()

            { TRACE("Entering XClassFactory ctor at %p\n", this); }

        virtual BOOL QueryInterface(int lRid, void** ppvObj);

        virtual DWORD Release();

        virtual DWORD AddRef();

        virtual BOOL CreateInstance(int lRid, void** ppvObj);

    } m_xClassFactory;

    friend class XClassFactory;

};

 

class CSpaceship : public CSimulatedCmdTarget

{

private:

    int m_nPosition; // We can access these from

                     //  all the interfaces

    int m_nAcceleration;

    int m_nColor;

public:

    CSpaceship() {

        TRACE("Entering CSpaceship ctor at %p\n", this);

        m_nPosition = 100;

        m_nAcceleration = 101;

        m_nColor = 102;

    }

    ~CSpaceship()

        { TRACE("Entering CSpaceship dtor at %p\n", this); }

    BOOL ExternalQueryInterface(int lRid, void** ppvObj);

    class XMotion : public IMotion

    {

    public:

        XMotion()

            { TRACE("Entering XMotion ctor at %p\n", this); }

        virtual BOOL QueryInterface(int lRid, void** ppvObj);

        virtual DWORD Release();

        virtual DWORD AddRef();

        virtual void Fly();

        virtual int& GetPosition();

    } m_xMotion;

 

class XVisual : public IVisual

    {

    public:

        XVisual() { TRACE("Entering XVisual at ctor\n"); }

        virtual BOOL QueryInterface(int lRid, void** ppvObj);

        virtual DWORD Release();

        virtual DWORD AddRef();

        virtual void Display();

    } m_xVisual;

 

    friend class XVisual;  // These must be at the bottom!

    friend class XMotion;

    friend class CSpaceshipFactory::XClassFactory;

};

 

 

Listing 2: The Spaceship.h file.

 

 

 

 

 

 

 

 

 

 

 

 

 

SPACESHIP.CPP

 

#include <stdio.h>

#include <stddef.h> // for offsetof in METHOD_PROLOGUE

#include <ASSERT.h>

#include "Interface.h"

#include "Spaceship.h"

 

CSpaceshipFactory g_factory;

 

//----------member functions-----------------------------------------

BOOL CSpaceshipFactory::ExternalQueryInterface(int nIid, void** ppvObj)

{

    TRACE("\nEntering CSpaceshipFactory::ExternalQueryInterface()--nIid =  %d\n", nIid);

    switch (nIid)

       {

              case IID_IUnknown:

              case IID_IClassFactory:

                     *ppvObj = &m_xClassFactory;

                     break;

              default:

                     *ppvObj = NULL;

        return FALSE;

    }

    ExternalAddRef();

    return TRUE;

}

 

BOOL CSpaceshipFactory::XClassFactory::QueryInterface(int nIid, void** ppvObj)

{

    TRACE("\nEntering CSpaceshipFactory::XClassFactory::QueryInterface()--nIid = %d\n", nIid);

    METHOD_PROLOGUE(CSpaceshipFactory, ClassFactory) // makes pThis

    return pThis->ExternalQueryInterface(nIid, ppvObj); // delegate to CSpaceshipFactory

}

 

BOOL CSpaceshipFactory::XClassFactory::CreateInstance(int nIid, void** ppvObj)

{

    TRACE("\nEntering CSpaceshipFactory::XClassFactory::CreateInstance()\n");

    METHOD_PROLOGUE(CSpaceshipFactory, ClassFactory) // makes pThis

    CSpaceship* pObj = new CSpaceship();

    if (pObj->ExternalQueryInterface(nIid, ppvObj))

       {

        pObj->ExternalRelease(); // balance reference count

        return TRUE;

    }

    return FALSE;

}

 

DWORD CSpaceshipFactory::XClassFactory::Release()

{

    TRACE("\nEntering CSpaceshipFactory::XClassFactory::Release()\n");

    METHOD_PROLOGUE(CSpaceshipFactory, ClassFactory) // makes pThis

    return pThis->ExternalRelease(); // delegate to CSimulatedCmdTarget

}

 

DWORD CSpaceshipFactory::XClassFactory::AddRef()

{

    TRACE("\nEntering CSpaceshipFactory::XClassFactory::AddRef()\n");

    METHOD_PROLOGUE(CSpaceshipFactory, ClassFactory) // makes pThis

    return pThis->ExternalAddRef(); // delegate to CSimulatedCmdTarget

}

 

BOOL CSpaceship::ExternalQueryInterface(int nIid, void** ppvObj)

{

    TRACE("\nEntering CSpaceship::ExternalQueryInterface()--nIid = %d\n", nIid);

    switch (nIid)

       {

              case IID_IUnknown:

              case IID_IMotion:

                     *ppvObj = &m_xMotion; // Both IMotion and IVisual are derived

                     break;                // from IUnknown, so either pointer will do

              case IID_IVisual:

                     *ppvObj = &m_xVisual;

                     break;

              default:

                     *ppvObj = NULL;

        return FALSE;

    }

    ExternalAddRef();

    return TRUE;

}

 

BOOL CSpaceship::XMotion::QueryInterface(int nIid, void** ppvObj)

{

    TRACE("\nEntering CSpaceship::XMotion::QueryInterface()--nIid =  %d\n", nIid);

    METHOD_PROLOGUE(CSpaceship, Motion) // makes pThis

    return pThis->ExternalQueryInterface(nIid, ppvObj); // delegate to CSpaceship

}

 

DWORD CSpaceship::XMotion::Release()

{

    TRACE("\nEntering CSpaceship::XMotion::Release()\n");

    METHOD_PROLOGUE(CSpaceship, Motion) // makes pThis

    return pThis->ExternalRelease(); // delegate to CSimulatedCmdTarget

}

 

DWORD CSpaceship::XMotion::AddRef()

{

    TRACE("\nEntering CSpaceship::XMotion::AddRef()\n");

    METHOD_PROLOGUE(CSpaceship, Motion) // makes pThis

    return pThis->ExternalAddRef(); // delegate to CSimulatedCmdTarget

}

 

void CSpaceship::XMotion::Fly()

{

    TRACE("\nEntering CSpaceship::XMotion::Fly()\n");

    METHOD_PROLOGUE(CSpaceship, Motion) // makes pThis

    TRACE("this pointer = %p, pThis pointer = %p\n", this, pThis);

    TRACE("m_nPosition = %d\n", pThis->m_nPosition);

    TRACE("m_nAcceleration = %d\n", pThis->m_nAcceleration);

}

 

int& CSpaceship::XMotion::GetPosition()

{

    TRACE("\nEntering CSpaceship::XMotion::GetPosition()\n");

    METHOD_PROLOGUE(CSpaceship, Motion) // makes pThis

    TRACE("this pointer = %p, pThis pointer = %p\n", this, pThis);

    TRACE("m_nPosition = %d\n", pThis->m_nPosition);

    TRACE("m_nAcceleration = %d\n", pThis->m_nAcceleration);

    return pThis->m_nPosition;

}

 

BOOL CSpaceship::XVisual::QueryInterface(int nIid, void** ppvObj)

{

    TRACE("\nEntering CSpaceship::XVisual::QueryInterface()--nIid =  %d\n", nIid);

    METHOD_PROLOGUE(CSpaceship, Visual) // makes pThis

    return pThis->ExternalQueryInterface(nIid, ppvObj); // delegate to

                                                        //  CSpaceship

}

 

DWORD CSpaceship::XVisual::Release()

{

    TRACE("\nEntering CSpaceship::XVisual::Release()\n");

    METHOD_PROLOGUE(CSpaceship, Visual) // makes pThis

    return pThis->ExternalRelease(); // delegate to CSimulatedCmdTarget

}

 

DWORD CSpaceship::XVisual::AddRef()

{

    TRACE("\nEntering CSpaceship::XVisual::AddRef()\n");

    METHOD_PROLOGUE(CSpaceship, Visual) // makes pThis

    return pThis->ExternalAddRef(); // delegate to CSimulatedCmdTarget

}

 

void CSpaceship::XVisual::Display()

{

    TRACE("\nEntering CSpaceship::XVisual::Display()\n");

    METHOD_PROLOGUE(CSpaceship, Visual) // makes pThis

    TRACE("this pointer = %p, pThis pointer = %p\n", this, pThis);

    TRACE("m_nPosition = %d\n", pThis->m_nPosition);

    TRACE("m_nColor = %d\n", pThis->m_nColor);

}

 

//----------simulates COM component -----------------------------------

// In real COM, this would be DllGetClassObject(), which would be called

//  whenever a client called CoGetClassObject()

 

BOOL GetClassObject(int nClsid, int nIid, void** ppvObj)

{

    ASSERT(nClsid == CLSID_CSpaceship);

    ASSERT((nIid == IID_IUnknown) || (nIid == IID_IClassFactory));

    return g_factory.ExternalQueryInterface(nIid, ppvObj);

    // Refcount is 2, which prevents accidental deletion

}

 

 

Listing 3:  The Spaceship.cpp file.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

CLIENT.CPP

 

#include <stdio.h>

#include <stddef.h> // for offsetof in METHOD_PROLOGUE

#include <assert.h>

#include "interface.h"

 

//----------main program-----------------------------------------------

int main() // simulates OLE client program

{

    TRACE("\nEntering client main()\n");

    IUnknown* pUnk; // If you declare these void*, you lose type-safety

    IMotion* pMot;

    IVisual* pVis;

    IClassFactory* pClf;

 

    GetClassObject(CLSID_CSpaceship, IID_IClassFactory, (void**) &pClf);

 

    pClf->CreateInstance(IID_IUnknown, (void**) &pUnk);

    pUnk->QueryInterface(IID_IMotion, (void**) &pMot); // All three

    pMot->QueryInterface(IID_IVisual, (void**) &pVis); //  pointers

                                                       //  should work

 

    TRACE("\nmain(): pUnk = %p, pMot = %p, pDis = %p\n\n", pUnk, pMot, pVis);

 

    // Test all the interface virtual functions

    pMot->Fly();

    int nPos = pMot->GetPosition();

    TRACE("nPos = %d\n", nPos);

    pVis->Display();

 

    pClf->Release();

    pUnk->Release();

    pMot->Release();

    pVis->Release();

    return 0;

}

 

 

Listing 4:  The Client.cpp file.

 

 

Continue on next module.....

 

 

 

 

 

 

Further reading and digging:

  1. DCOM at MSDN.

  2. COM+ at MSDN.

  3. COM at MSDN.

  4. Win32 process, thread and synchronization story can be found starting from Module R.

  5. MSDN MFC 7.0 class library online documentation.

  6. MSDN MFC 9.0 class library online documentation - latest version.

  7. MSDN Library

  8. Windows data type.

  9. Win32 programming Tutorial.

  10. The best of C/C++, MFC, Windows and other related books.

  11. Unicode and Multibyte character set: Story and program examples.

 

 


 

| Tenouk C & C++ | MFC Home | COM Part 1 | COM Part 3 | Download | Site Index |