The following files show code for a working "simulated COM" program, Ex22a. 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
// 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 %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 %p\n", this); } virtual BOOL CreateInstance(int nIid, void** ppvObj) = 0; }; struct IMotion : public IUnknown { IMotion() { TRACE("Entering IMotion ctor %p\n", this); } virtual void Fly() = 0; // pure virtual int& GetPosition() = 0; }; struct IVisual : public IUnknown { IVisual() { TRACE("Entering IVisual ctor %p\n", this); } virtual void Display() = 0; }; class CSimulatedCmdTarget // 'simulated' CSimulatedCmdTarget { public: DWORD m_dwRef; protected: CSimulatedCmdTarget() { TRACE("Entering CSimulatedCmdTarget ctor %p\n", this); m_dwRef = 1; // implied first AddRef } virtual ~CSimulatedCmdTarget() { TRACE("Entering CSimulatedCmdTarget dtor %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; } };
class CSpaceship; //----------class declarations------------------------------------- class CSpaceshipFactory : public CSimulatedCmdTarget { public: CSpaceshipFactory() { TRACE("Entering CSpaceshipFactory ctor %p\n", this); } ~CSpaceshipFactory() { TRACE("Entering CSpaceshipFactory dtor %p\n", this); } BOOL ExternalQueryInterface(int lRid, void** ppvObj); class XClassFactory : public IClassFactory { public: XClassFactory() { TRACE("Entering XClassFactory ctor %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 %p\n", this); m_nPosition = 100; m_nAcceleration = 101; m_nColor = 102; } ~CSpaceship() { TRACE("Entering CSpaceship dtor %p\n", this); } BOOL ExternalQueryInterface(int lRid, void** ppvObj); class XMotion : public IMotion { public: XMotion() { TRACE("Entering XMotion ctor %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 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; };
#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( "Entering 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("Entering 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("Entering 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("Entering CSpaceshipFactory::XClassFactory::Release\n"); METHOD_PROLOGUE(CSpaceshipFactory, ClassFactory) // makes pThis return pThis->ExternalRelease(); // delegate to CSimulatedCmdTarget } DWORD CSpaceshipFactory::XClassFactory::AddRef() { TRACE("Entering CSpaceshipFactory::XClassFactory::AddRef\n"); METHOD_PROLOGUE(CSpaceshipFactory, ClassFactory) // makes pThis return pThis->ExternalAddRef(); // delegate to CSimulatedCmdTarget } BOOL CSpaceship::ExternalQueryInterface(int nIid, void** ppvObj) { TRACE("Entering 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("Entering 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("Entering CSpaceship::XMotion::Release\n"); METHOD_PROLOGUE(CSpaceship, Motion) // makes pThis return pThis->ExternalRelease(); // delegate to CSimulatedCmdTarget } DWORD CSpaceship::XMotion::AddRef() { TRACE("Entering CSpaceship::XMotion::AddRef\n"); METHOD_PROLOGUE(CSpaceship, Motion) // makes pThis return pThis->ExternalAddRef(); // delegate to CSimulatedCmdTarget } void CSpaceship::XMotion::Fly() { TRACE("Entering CSpaceship::XMotion::Fly\n"); METHOD_PROLOGUE(CSpaceship, Motion) // makes pThis TRACE("this = %p, pThis = %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("Entering CSpaceship::XMotion::GetPosition\n"); METHOD_PROLOGUE(CSpaceship, Motion) // makes pThis TRACE("this = %p, pThis = %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("Entering 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("Entering CSpaceship::XVisual::Release\n"); METHOD_PROLOGUE(CSpaceship, Visual) // makes pThis return pThis->ExternalRelease(); // delegate to CSimulatedCmdTarget } DWORD CSpaceship::XVisual::AddRef() { TRACE("Entering CSpaceship::XVisual::AddRef\n"); METHOD_PROLOGUE(CSpaceship, Visual) // makes pThis return pThis->ExternalAddRef(); // delegate to CSimulatedCmdTarget } void CSpaceship::XVisual::Display() { TRACE("Entering CSpaceship::XVisual::Display\n"); METHOD_PROLOGUE(CSpaceship, Visual) // makes pThis TRACE("this = %p, pThis = %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 }
#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("Entering 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("main: pUnk = %p, pMot = %p, pDis = %p\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; }