The Ex22a Example: Simulated COM
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




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 %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; }
};
Spaceship.h
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;
};
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(
"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
}
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("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;
}