Figure 1   MTObject.h


 //////////////////////////////////////////////////////
 // 
 // MTObject.h - 1995-1996, Don Box 
 //
 // Helper base class for serializing access to an object
 //
 
 #ifndef _MTOBJECT_H
 #define _MTOBJECT_H
 
 class MTObject 
 {
 public:
     MTObject();
     ~MTObject(); // note: destructor is non-virtual, so don't inherit publicly
 
 // helper class to lock object in an exception-safe manner
     class Acquire {
     public:
         Acquire(const MTObject* pThis, DWORD dwTimeOut = INFINITE);
         ~Acquire();
         BOOL IsLocked() const;
     private:
         const MTObject*     m_object;       // pointer to locked object
         BOOL                m_bLocked;      // lock acquired?
     };
     friend class Acquire;
 private:
     HANDLE                  m_hmutexLock;   // MT-safety for this object
 };
 
 // create the mutex
 inline MTObject::MTObject()
 :   m_hmutexLock(CreateMutex(0, FALSE, 0))
 {
 }
 
 // destroy the mutex
 inline MTObject::~MTObject()
 {
     CloseHandle(m_hmutexLock);
 }
 
 // acquire the mutex for pThis
 inline MTObject::Acquire::Acquire(const MTObject* pThis, DWORD dwTimeOut)
 : m_object(pThis), m_bLocked(FALSE)
 {
     if (WaitForSingleObject(m_object->m_hmutexLock,dwTimeOut)= =WAIT_OBJECT_0)
         m_bLocked = TRUE;
 }
 
 // release the mutex for pThis
 inline MTObject::Acquire::~Acquire()
 {
     if (m_bLocked)
         ReleaseMutex(m_object->m_hmutexLock);
 }
 
 // test the mutex acquisition
 inline BOOL MTObject::Acquire::IsLocked() const
 {
     return m_bLocked;
 }
 
 #endif

Figure 4   Multithreaded Lifetime Control

CoModuleLifetime.h


 //////////////////////////////////////////////////////
 // 
 // CoModuleLifetime.h - 1995-1996, Don Box 
 //
 // Multithreaded implementation of LockServer
 //
 
 #ifndef _MODLIFE_H
 #define _MODLIFE_H
 
 #include "MTObject.h"
 
 // an abstract interface for controlling server lifetime from within
 interface IModuleLifetime : public IUnknown
 {
     STDMETHOD(RegisterManagedApartment)(DWORD *pdwAptID) PURE;
     STDMETHOD(RevokeManagedApartment)(DWORD dwAptID) PURE;
     STDMETHOD(TerminateAllApartments)(WPARAM wParam) PURE;
     STDMETHOD(IsTerminating)() PURE;
 
     STDMETHOD(LockServer)(BOOL bLock, BOOL bLastReleaseTerminates) PURE;
     STDMETHOD(CanUnloadNow)(void) PURE;
     STDMETHOD(ApartmentListen)(void) PURE;
 };
 
 // {261A26C0-2452-11cf-93E8-00C04FDE43AE}
 DEFINE_GUID(IID_IModuleLifetime, 
 0x261a26c0, 0x2452, 0x11cf, 0x93, 0xe8, 0x0, 0xc0, 0x4f, 0xde, 0x43, 0xae);
 
 // an implementation for controlling server lifetime from within
 class CoModuleLifetime: protected MTObject,       // free threaded instance
                         public    IModuleLifetime // of one interface
 {
 public:
     CoModuleLifetime();
     virtual ~CoModuleLifetime();
 
 // IUnknown methods
     STDMETHODIMP QueryInterface(REFIID riid, void**ppv);
     STDMETHODIMP_(ULONG) AddRef();
     STDMETHODIMP_(ULONG) Release();
 
 // IModuleLifetme methods
     STDMETHODIMP RegisterManagedApartment(DWORD *pdwAptID);
     STDMETHODIMP RevokeManagedApartment(DWORD dwAptID);
     STDMETHODIMP TerminateAllApartments(WPARAM wParam);
     STDMETHODIMP IsTerminating();
 
     STDMETHODIMP LockServer(BOOL bLock, BOOL bLastReleaseTerminates);
     STDMETHODIMP CanUnloadNow(void);
     STDMETHODIMP ApartmentListen(void);
 
 protected:
 // helper to find or create an empty slot
     size_t GetEmptySlot();
 private:
 // apartment manager members
     LONG            m_cLocks;           // the standard module lock count
     BOOL            m_bTerminating;     // WM_QUIT message has been posted
     size_t          m_nApartments;      // apartment capacity
     DWORD          *m_rgdwThreadIDs;    // array of thread IDs
 };
 
 #endif

CoModuleLifetime.cpp


 //////////////////////////////////////////////////////
 // 
 // CoModuleLifetime.cpp - 1995-1996, Don Box 
 //
 // Multithreaded implementation of LockServer
 //
 
 #include <windows.h>
 #include "CoModuleLifetime.h"
 
 CoModuleLifetime::CoModuleLifetime()
 :   m_cLocks(0),
     m_bTerminating(FALSE),
     m_nApartments(16),
     m_rgdwThreadIDs(new DWORD[m_nApartments])
 {
     memset(m_rgdwThreadIDs, 0, m_nApartments * sizeof(DWORD));
 }
 
 CoModuleLifetime::~CoModuleLifetime()
 {
     delete[] m_rgdwThreadIDs;
 }
 
 // IModuleLifetme methods
 
 // register the current thread to be terminated when all module locks go away
 STDMETHODIMP 
 CoModuleLifetime::RegisterManagedApartment(DWORD *pdwAptID)
 {
     Acquire lock(this);
     if (!lock.IsLocked())
         return E_FAIL;
     size_t i = GetEmptySlot();
     if (i = = size_t(-1))
         return E_FAIL;
     m_rgdwThreadIDs[i] = GetCurrentThreadId();
     *pdwAptID = i;
     return S_OK;
 }
 
 
 // remove the desired thread from the array
 STDMETHODIMP 
 CoModuleLifetime::RevokeManagedApartment(DWORD dwAptID)
 {
     Acquire lock(this);
     if (!lock.IsLocked() || dwAptID >= m_nApartments)
         return E_FAIL;
     m_rgdwThreadIDs[dwAptID] = 0;
     return S_OK;
 }
 
 // post a WM_QUIT message to all managed threads using PostThreadMessage
 STDMETHODIMP 
 CoModuleLifetime::TerminateAllApartments(WPARAM wParam)
 {
     Acquire lock(this);
     if (!lock.IsLocked())
         return E_FAIL;
     m_bTerminating = TRUE;
     for (size_t i = 0; i < m_nApartments; i++)
         if (m_rgdwThreadIDs[i] != 0)
             PostThreadMessage(m_rgdwThreadIDs[i], WM_QUIT, wParam, 0);
     return S_OK;
 }
 
 // indicate the fact that the WM_QUIT message is pending to prevent
 // any last minute activity
 STDMETHODIMP 
 CoModuleLifetime::IsTerminating()
 {
     Acquire lock(this);
     if (!lock.IsLocked())
         return E_FAIL;
     return m_bTerminating ? S_OK : S_FALSE;
 }
 
 // increment or decrement module lock count, potentially calling
 // TerminateAllThreads.  All implementations of IClassFactory::LockServer
 // should forward to this function, in addition to the constructor and 
 // destructor of all tracked objects
 STDMETHODIMP 
 CoModuleLifetime::LockServer(BOOL bLock, BOOL bLastReleaseTerminates)
 {
     Acquire lock(this);
     if (!lock.IsLocked())
         return E_FAIL;
     m_cLocks += bLock ? 1 : -1;
     if (m_cLocks = = 0 && bLastReleaseTerminates && !bLock)
         TerminateAllApartments(0);
     return S_OK;
 }
 
 // indicate the viability of this server
 STDMETHODIMP 
 CoModuleLifetime::CanUnloadNow(void)
 {
     Acquire lock(this);
     if (!lock.IsLocked())
         return E_FAIL;
     return (m_cLocks = = 0) ? S_OK : S_FALSE;    
 }
 
 // run a simple message pump for the calling thread (note no lock is acquired)
 STDMETHODIMP 
 CoModuleLifetime::ApartmentListen(void)
 {
     MSG msg;
     while (GetMessage(&msg, 0, 0, 0))
         DispatchMessage(&msg);
     return S_OK;
 }
 
 // find an empty slot, potentially growing the array (assumes already locked)
 size_t
 CoModuleLifetime::GetEmptySlot()
 {
 // search for empty slot
     for (size_t i = 0; i < m_nApartments; i++)
         if (m_rgdwThreadIDs[i] = = 0)
             return i;
     
 // if no empty slot is found, then grow array
     size_t result = size_t(-1);
     enum { GROWBY = 16 };
     DWORD* rgNew = new DWORD[m_nApartments + GROWBY];
     if (rgNew)
     {
         memcpy(rgNew, m_rgdwThreadIDs, m_nApartments * sizeof(DWORD));
         memset(rgNew + m_nApartments, 0, GROWBY * sizeof(DWORD));
         delete[] m_rgdwThreadIDs;
         result = m_nApartments;
         m_rgdwThreadIDs = rgNew;
         m_nApartments += GROWBY;
     }
     return result;
 }
 
 // IUnknown methods
 STDMETHODIMP 
 CoModuleLifetime::QueryInterface(REFIID riid, void**ppv)
 {
     if (riid = = IID_IUnknown || riid = = IID_IModuleLifetime)
         *ppv = (IModuleLifetime *)this;
     else
         *ppv = 0;
     if (*ppv)
         ((IUnknown*)*ppv)->AddRef();
     return *ppv ? S_OK : E_NOINTERFACE;
 }
 
 STDMETHODIMP_(ULONG) 
 CoModuleLifetime::AddRef()
 {
     return 2;
 }
 
 STDMETHODIMP_(ULONG) 
 CoModuleLifetime::Release()
 {
     return 1;
 }

Figure 5  CoClassFactory

CoClassFactory.h


 //////////////////////////////////////////////////////
 // 
 // CoClassFactory.h - 1995-1996, Don Box 
 //
 // Generic Class Factory template using IModuleLifetime
 //
 
 #ifndef _COCF_H
 #define _COCF_H
 
 #include "MTObject.h"
 #include "CoModuleLifetime.h"
 
 // common implementation of a generic class factory
 class CoClassFactoryBase : public IClassFactory
 {
 public:
     CoClassFactoryBase(IModuleLifetime *pml);
     virtual ~CoClassFactoryBase();
 
 // IUnknown methods
     STDMETHODIMP QueryInterface(REFIID riid, void**ppv);
     STDMETHODIMP_(ULONG) AddRef();
     STDMETHODIMP_(ULONG) Release();
 
 // IClassFactory methods (CreateInstance left for derived implementations)
     STDMETHODIMP LockServer(BOOL bLock);
 
 protected:
     IModuleLifetime*    m_pml;                  // Lifetime ctl for LockServer
 };
 
 // a parameterized implementation of IClassFactory with no aggregation support
 template <class CoClass>
 class CoClassFactory : public CoClassFactoryBase 
 {
 public:
     CoClassFactory(IModuleLifetime *pml)
     : CoClassFactoryBase(pml)
     {
     }
 
     STDMETHODIMP CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv)
     {
         *ppv = 0;
         if (pUnkOuter) return CLASS_E_NOAGGREGATION;
         CoClass *p = new CoClass;
         if (!p) return E_OUTOFMEMORY;
         HRESULT hr = p->QueryInterface(riid, ppv);
         if (FAILED(hr)) delete p;
         return hr;
     }
 };
 
 #endif

CoClassFactory.cpp


 //////////////////////////////////////////////////////
 // 
 // CoClassFactory.cpp - 1995-1996, Don Box 
 //
 // Generic Class Factory template using IModuleLifetime
 //
 
 #include <windows.h>
 #include "CoClassFactory.h"
 
 CoClassFactoryBase::CoClassFactoryBase(IModuleLifetime *pml)
 : m_pml(pml)
 {
 }
 
 CoClassFactoryBase::~CoClassFactoryBase()
 {
 }
 
 // simply forward to module lifetime
 STDMETHODIMP CoClassFactoryBase::LockServer(BOOL bLock) 
 {
     if (m_pml)
         return m_pml->LockServer(bLock, TRUE);
     return E_NOTIMPL;
 }
 
 STDMETHODIMP CoClassFactoryBase::QueryInterface(REFIID riid, void**ppv)
 {
     if (riid = = IID_IUnknown || riid = = IID_IClassFactory)
         *ppv = (IClassFactory*)this;
     else 
         *ppv = 0;
     if (*ppv)
         ((IUnknown*)(*ppv))->AddRef();
     return *ppv ? S_OK : E_NOINTERFACE;
 }
 
 STDMETHODIMP_(ULONG) CoClassFactoryBase::AddRef() 
 {
     return 2;
 }
 
 STDMETHODIMP_(ULONG) CoClassFactoryBase::Release() 
 {
     return 1;
 }

Figure 7   dwDestContext Parameters

MSHCTX_LOCAL The client and object are in different address spaces on the same host machine, and some mechanism for using shared memory is available from the OS.
MSHCTX_NOSHAREDMEM The client and object are in different address spaces on the same host machine, and no mechanism for using shared memory is available from the OS.
MSHCTX_DIFFERENTMACHINE The client and object are in different address spaces on different host machines.
MSHCTX_INPROC The client and object are in the same address space on the same host machine, but are different apartments (threads).


Figure 8   CoMultiplexingCF

CoMultiplexingCF.h


 //////////////////////////////////////////////////////
 // 
 // CoMultiplexingCF.h - 1995-1996, Don Box 
 //
 // Multiplexing Class Factory for Multi-Apartment COM Classes
 //
 
 #ifndef _COMUXCF_H
 #define _COMUXCF_H
 
 #include "CoClassFactory.h"
 
 // an implementation of IClassFactory that multiplexes its CreateInstance
 // to one of N class factories marshalled in from other apartments
 class CoMultiplexingCF : protected MTObject,    
                          public    CoClassFactoryBase 
 {
 public:
     CoMultiplexingCF(long nApartments, IModuleLifetime* pml);
     virtual ~CoMultiplexingCF();
 
 // the multiplexing CreateInstance that forwards to the "next" apartment
     STDMETHODIMP CreateInstance(IUnknown *pUnkOuter,REFIID riid,void** ppvObj);
 
 // marshal in a ClassFactory to receive 1 out of N CreateInstance calls
     STDMETHODIMP RegisterInternalApartment(IClassFactory *pcf);
 
 // wait until N apartments have been internally unmarshaled
     STDMETHODIMP WaitForInternalApartmentsToRegister(long nHowMany = -1,
                                                      DWORD dwTimeOut=INFINITE);
 
 // release the proxys for all marshalled class factories
     STDMETHODIMP RevokeInternalApartments();
     
 // register this object with the SCM
     STDMETHODIMP RegisterExternalApartment(REFCLSID rclsid,
                                            DWORD dwClsCtx=CLSCTX_LOCAL_SERVER,
                                            DWORD dwFlags = REGCLS_MULTIPLEUSE);
 
 // revoke this object from the SCM
     STDMETHODIMP RevokeExternalApartment();
 
 protected:
 // virtual upcall to allow for arbitrary scheduling
     virtual size_t GetNextApartment();
     size_t GetApartmentCount() const;
 private:
 // helper function to marshal the interface into the correct stream
     HRESULT MarshalClassObject(IClassFactory *pcf, DWORD dwAptIndex);
 
 // helper function to unmarshal the interface from the stream 
     HRESULT UnmarshalClassObject(DWORD dwAptIndex);
 
 // helper function that asserts that the calling thread is the "main" apartment
     BOOL    IsExternalApartment();
 
 // multithreading data members
     HANDLE              m_hsemMarshaled;        // Count of pending marshals
     DWORD               m_dwExternalThreadID;   // ID of externalized apartment
 // multiplexor data members
     size_t              m_nFactories;           // Capacity of arrays
     IClassFactory**     m_rgpFactories;         // class factory array
     IStream**           m_rgpStreams;           // stream array for marshaling
     size_t              m_nNextIndex;           // round-robin multiplexor
 
 // normal class object data members
     DWORD               m_dwReg;                // key for CoRevokeClassObject
 };
 
 #endif

CoMultiplexingCF.cpp


 //////////////////////////////////////////////////////
 // 
 // CoMultiplexingCF.cpp - 1995-1996, Don Box 
 //
 // Multiplexing Class Factory for Multi-Apartment COM Classes
 //
 
 #include <windows.h>
 #include "CoMultiplexingCF.h"
 
 CoMultiplexingCF::CoMultiplexingCF(long nApartments,   
                                    IModuleLifetime *pml)
 :   CoClassFactoryBase(pml),
     m_hsemMarshaled(CreateSemaphore(0, 0, nApartments, 0)),
     m_dwExternalThreadID(0),
     m_nFactories(nApartments),
     m_rgpFactories(new IClassFactory *[nApartments]),
     m_rgpStreams(new IStream *[nApartments]),
     m_nNextIndex(0)
 {
     memset(m_rgpFactories, 0, sizeof(*m_rgpFactories) * nApartments);
     memset(m_rgpStreams, 0, sizeof(*m_rgpStreams) * nApartments);
 }
 
 CoMultiplexingCF::~CoMultiplexingCF()
 {
     CloseHandle(m_hsemMarshaled);
     delete [] m_rgpFactories;
     delete [] m_rgpStreams;
 }
 
 inline size_t 
 CoMultiplexingCF::GetApartmentCount() const
 {
     return m_nFactories;
 }
 
 // scheduler for multiplexor (this implementation uses round-robin scheduling)
 size_t 
 CoMultiplexingCF::GetNextApartment()
 {
     size_t result = m_nNextIndex++;
     m_nNextIndex %= GetApartmentCount();
     return result;
 }
 
 // helper to thread-marshal the interface into the appropriate stream
 inline HRESULT 
 CoMultiplexingCF::MarshalClassObject(IClassFactory *pcf, DWORD dwAptIndex)
 {
     return CoMarshalInterThreadInterfaceInStream(IID_IClassFactory,
                                                  pcf,
                                                  &m_rgpStreams[dwAptIndex]);
 }
 
 // helper to thread-unmarshal the interface from the appropriate stream
 inline HRESULT 
 CoMultiplexingCF::UnmarshalClassObject(DWORD dwAptIndex)
 {
     HRESULT hr;
     hr = CoGetInterfaceAndReleaseStream(m_rgpStreams[dwAptIndex],	
                                         IID_IClassFactory,	
                                 (void**)&m_rgpFactories[dwAptIndex]);
     m_rgpStreams[dwAptIndex] = 0;
     return hr;
 }
 
 inline BOOL   
 CoMultiplexingCF::IsExternalApartment()
 {
     if (m_dwExternalThreadID = = 0) // first time through
         m_dwExternalThreadID = GetCurrentThreadId();
     else if (m_dwExternalThreadID != GetCurrentThreadId())
         return FALSE;
     return TRUE;
 }
 
 // multiplex to one of N class factories
 STDMETHODIMP 
 CoMultiplexingCF::CreateInstance(IUnknown*pUnkOuter, REFIID riid,void** ppvObj)
 {
     HRESULT result = E_FAIL;
 // artificially lock this module to prevent early termination
     LockServer(TRUE);
 // ensure that this module isn't about to die
     if (m_pml = = 0 || m_pml->IsTerminating() = = S_FALSE)
     {
         Acquire lock(this);
         if (lock.IsLocked())
         {    
 // select the next apartment and go for it!
             IClassFactory *pcf = m_rgpFactories[GetNextApartment()];
             if (pcf)
                 result = pcf->CreateInstance(pUnkOuter, riid, ppvObj);
         }
     }
 // release artificial module lock
     LockServer(FALSE);
     return result;
 }
 
 // marshal in a class factory from one of the multiplexed apartments
 STDMETHODIMP 
 CoMultiplexingCF::RegisterInternalApartment(IClassFactory *pcf)
 {
     SCODE result = E_FAIL;
     Acquire lock(this);
     if (lock.IsLocked())
     {
         const DWORD BAD_INDEX = 0xFFFFFFFF; // rogue value
         DWORD dwThisApartment = BAD_INDEX; 
 
 // try to find an empty slot
         for (DWORD i = 0; 
              i < m_nFactories && dwThisApartment = = BAD_INDEX; 
              i++)
             if (m_rgpStreams[i] = = 0 && m_rgpFactories[i] = = 0)
                 dwThisApartment = i;
     
 // marshal in the interface and indicate that it is there 
 // by releasing a semaphore
         if (dwThisApartment != BAD_INDEX)
         {
             result = MarshalClassObject(pcf, dwThisApartment);
             if (SUCCEEDED(result))
                 ReleaseSemaphore(m_hsemMarshaled, 1, 0);
         }
     }
     return result;
 }
 
 // allow the main apartment to wait for N apartments to register by
 // waiting for N semaphores to be released.  As each semaphore is released,
 // unmarshal the interface (note: this must be called by the same thread
 // that calls RegisterExternalApartment)
 STDMETHODIMP 
 CoMultiplexingCF::WaitForInternalApartmentsToRegister(long nHowMany,
                                                       DWORD dwTimeOut)
 {
     if (!IsExternalApartment())  return E_FAIL;
 // default to array capacity
     if (nHowMany = = -1) 
         nHowMany = m_nFactories;
 // unmarshal nHowMany interfaces
     for (long i = 0; i < nHowMany; i++)
     {
 // wait for a semaphore to be released by another apartment
         DWORD dwResult = WaitForSingleObject(m_hsemMarshaled, dwTimeOut);
         if (dwResult != WAIT_OBJECT_0)
             return E_FAIL;
 // grab the instance lock
         Acquire lock(this);
         if (!lock.IsLocked())
             return E_FAIL;
 // find the first interface that needs to be unmarshalled
         for (size_t index = 0; index < m_nFactories; index++)
         {
             if (m_rgpStreams[index] != 0)         // found one!
             {
                 UnmarshalClassObject(index);  // unmarshal it
                 break;
             }
         }
     }
     return S_OK;
 }
     
 // release the proxys for all marshalled class objects
 STDMETHODIMP 
 CoMultiplexingCF::RevokeInternalApartments()
 {
     if (!IsExternalApartment())  
         return E_FAIL;
     Acquire lock(this);
     if (!lock.IsLocked()) 
         return E_FAIL;
     for (size_t i = 0; i < m_nFactories; i++)
         if (m_rgpFactories[i] != 0)
             m_rgpFactories[i]->Release();
     memset(m_rgpFactories, 0, sizeof(*m_rgpFactories) * m_nFactories);
     return S_OK;
 }
 
 // register this object with the SCM using CoRegisterClassObject
 STDMETHODIMP 
 CoMultiplexingCF::RegisterExternalApartment(REFCLSID rclsid,
                                             DWORD dwClsCtx,
                                             DWORD dwFlags)
 {
     if (!IsExternalApartment())  return E_FAIL;
     return CoRegisterClassObject(rclsid, (IClassFactory*)this,
                                  dwClsCtx, dwFlags, &m_dwReg);
 }
 
 // revoke this object from the SCM using CoRevokeClassObject
 STDMETHODIMP 
 CoMultiplexingCF::RevokeExternalApartment()
 {
     if (!IsExternalApartment())  return E_FAIL;
     return CoRevokeClassObject(m_dwReg);
 }

Figure 9   CoSleeper

Sleep.odl


 [
     uuid(227AE720-0E31-11cf-93E8-00C04FDE43AE),
     lcid(9),
     version(1.0),
     helpstring("Sleepy COM Objects")
 ]
 library Sleepy
 {
     importlib("stdole32.tlb");
     [
         uuid(227AE721-0E31-11cf-93E8-00C04FDE43AE),
         odl,
         dual,
         helpstring("A simple interface that implements sleep")
     ]
     interface ISleep : IDispatch
     {
         [ 
             id(10),
             helpstring("Sleep for msecs and return the actual msecs slept")
         ] 
         HRESULT Sleep([in] long msecs, [out, retval] long *presult);
 
         [ 
             id(11), 
             propget,
             helpstring("Returns the logical apartment of the object")
         ] 
         HRESULT ApartmentNumber([out, retval] long *presult);
 
         [ 
             id(12), 
             propget,
             helpstring("Returns the physical thread ID of the object")
         ] 
         HRESULT ThreadID([out, retval] long *presult);
     }
 
     [
         uuid(227AE722-0E31-11cf-93E8-00C04FDE43AE),
         helpstring("A simple implementation of the ISleep interface")
     ]
     coclass CoSleeper 
     {
         [default] interface ISleep;
     }
 
 }
CoSleeper.h

 //////////////////////////////////////////////////////
 // 
 // CoSleeper.h - 1995-1996, Don Box 
 //
 // A Sleep Object exposed through a dual interface
 //
 
 #ifndef _COSLEEPER_H
 #define _COSLEEPER_H
 
 #include "Sleep.h"
 
 class CoSleeper : public ISleep 
 {
 public:
     CoSleeper();
     virtual ~CoSleeper();
 
 // IUnknown methods
     STDMETHODIMP QueryInterface(REFIID, void**);
     STDMETHODIMP_(ULONG) AddRef();
     STDMETHODIMP_(ULONG) Release();
 
 // IDispatch methods
     STDMETHODIMP GetTypeInfoCount(UINT*);
     STDMETHODIMP GetTypeInfo(UINT, LCID, ITypeInfo**);
     STDMETHODIMP GetIDsOfNames(REFIID, OLECHAR**, UINT, LCID, DISPID *);
     STDMETHODIMP Invoke(DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*, 
                         EXCEPINFO*, UINT*);
 
 // ISleep methods
     STDMETHODIMP Sleep(long msecs, long * presult);
     STDMETHODIMP get_ApartmentNumber(long * presult);
     STDMETHODIMP get_ThreadID(long * presult);
 private:
     ULONG       m_cRef;
     ITypeInfo*  m_pTypeInfo;
 };
 
 #endif
CoSleeper.cpp

 //////////////////////////////////////////////////////
 // 
 // CoSleeper.cpp - 1995-1996, Don Box 
 //
 // A Sleep Object exposed through a dual interface
 //
 
 #include <windows.h>
 #include "CoSleeper.h"
 
 extern void SvcLock();
 extern void SvcUnlock();
 extern DWORD GetCurrentApartmentNo();
 
 // load our type information to implement IDispatch
 CoSleeper::CoSleeper()
 : m_cRef(0),
   m_pTypeInfo(0)
 {
     SvcLock();
     ITypeLib *ptl = 0;
     if (FAILED(LoadRegTypeLib(LIBID_Sleepy, 1, 0, 9, &ptl)))
         LoadTypeLib(OLESTR("Sleep.tlb"), &ptl);
     if (ptl)
     {
         ptl->GetTypeInfoOfGuid(IID_ISleep, &m_pTypeInfo);
         ptl->Release();
     }
 }
 
 // unload our type information
 CoSleeper::~CoSleeper()
 {
     if (m_pTypeInfo)
         m_pTypeInfo->Release();
     SvcUnlock();
 }
 
 // ISleep methods ////////////////////////////////////
 
 // sleep for msecs using the API function Sleep
 STDMETHODIMP 
 CoSleeper::Sleep(long msecs, long * presult)
 {
     DWORD dwTicksStart = GetTickCount();
     ::Sleep(msecs);
     *presult = GetTickCount() - dwTicksStart;
     return S_OK;
 }
 
 // return the logical apartment number that this object lives in
 STDMETHODIMP 
 CoSleeper::get_ApartmentNumber(long * presult)
 {
     *presult = GetCurrentApartmentNo();
     return S_OK;
 }
 
 // return the physical thread that this object lives on
 STDMETHODIMP 
 CoSleeper::get_ThreadID(long * presult)
 {
     *presult = GetCurrentThreadId();
     return S_OK;
 }
 
 // IDispatch methods /////////////////////////////////
 STDMETHODIMP 
 CoSleeper::GetTypeInfoCount(UINT *pctinfo)
 {
     if (pctinfo = = 0) return E_POINTER;
     *pctinfo = 1;
     return S_OK;
 }
 
 STDMETHODIMP 
 CoSleeper::GetTypeInfo(UINT index, LCID, ITypeInfo**ppti)
 {
     if (ppti = = 0) return E_POINTER;
     if (index != 0 || m_pTypeInfo = = 0) return DISP_E_BADINDEX;
     (*ppti = m_pTypeInfo)->AddRef();
     return S_OK;
 }
 
 STDMETHODIMP 
 CoSleeper::GetIDsOfNames(REFIID, OLECHAR** rgsz, UINT n, LCID, DISPID* rgdid)
 {
     if (m_pTypeInfo = = 0) return E_FAIL;
     return m_pTypeInfo->GetIDsOfNames(rgsz, n, rgdid);
 }
 
 STDMETHODIMP 
 CoSleeper::Invoke(DISPID dispid, REFIID,LCID lcid, WORD wFlag, DISPPARAMS *pdp,
                   VARIANT *pVarResult, EXCEPINFO *pei, UINT *puArgErr)
 {
     if (m_pTypeInfo = = 0) return E_FAIL;
     return m_pTypeInfo->Invoke((ISleep*)this, dispid, wFlag, pdp,
                                 pVarResult, pei, puArgErr);
 }
 
 // IUnknown methods //////////////////////////////////
 STDMETHODIMP 
 CoSleeper::QueryInterface(REFIID riid, void**ppv)
 {
     if (riid = = IID_IUnknown || riid = = IID_IDispatch || riid = = IID_ISleep)
         *ppv = (ISleep*)this;
     else
         *ppv = 0;
 
     if (*ppv)
         ((IUnknown*)*ppv)->AddRef();
     return *ppv ? S_OK : E_NOINTERFACE;
 }
 
 STDMETHODIMP_(ULONG) 
 CoSleeper::AddRef()
 {
     return ++m_cRef;
 }
 
 STDMETHODIMP_(ULONG) 
 CoSleeper::Release()
 {
     if (--m_cRef = = 0)
     {
         delete this;
         return 0;
     }
     return m_cRef;
 }

Figure 10   AptSvc.cpp


 //////////////////////////////////////////////////////
 // 
 // AptSvc.cpp - 1995-1996, Don Box 
 //
 // Multithreaded OLE Server
 //
 
 #define INITGUID
 #include <windows.h>
 #include "CoMultiplexingCF.h"
 #include "CoSleeper.h"
 
 // change this constant to get more concurrency
 enum { NAPARTMENTS = 3 };
 
 // define a single instance of server lifetime control
 CoModuleLifetime  g_module;
 
 // define a multiplexing CF for each COM class
 CoMultiplexingCF  g_multiplexor(NAPARTMENTS, &g_module);
 
 // map extern calls to our module lifetime
 void SvcLock()
 {   g_module.LockServer(TRUE, TRUE);
 }
 
 // map extern calls to our module lifetime
 void SvcUnlock()
 {   g_module.LockServer(FALSE, TRUE);
 }
 
 // declare the apartment ID as a TLS variable
 __declspec(thread) DWORD gt_dwApartmentNumber = 0xFFFFFFFF;
 
 // simply return the TLS variable
 DWORD GetCurrentApartmentNo()
 {   return gt_dwApartmentNumber;
 }
 
 // AptThreadProc is a simple function that registers a class factory
 // with a multiplexor and wait around for things to happen
 DWORD WINAPI AptThreadProc(void*)
 {
 // initialize this thread as an OLE apartment
     CoInitialize(0);
 
 // register this thread to receive WM_QUIT messages and init apartment ID
     g_module.RegisterManagedApartment(&gt_dwApartmentNumber);
 
 // register a class object with the multiplexor
     CoClassFactory<CoSleeper> classObject(&g_module);
     g_multiplexor.RegisterInternalApartment(&classObject);
 
 // run a message pump
     MSG msg;
     while (GetMessage(&msg, 0, 0, 0))
         DispatchMessage(&msg);
 
 // cleanup
     g_module.RevokeManagedApartment(gt_dwApartmentNumber);
     CoUninitialize();
     return msg.wParam;
 }
 
 // MainThreadProc is a function that creates several worker threads
 // and registers a multiplexor as a class factory with the SCM, All 
 // incoming instantiation requests come in through this thread and
 // are dispatched via the multiplexor to the appropriate worker thread
 DWORD WINAPI MainThreadProc(void*)
 {
 // initialize this thread as an OLE apartment
     CoInitialize(0);
 // register this thread to receive WM_QUIT messages and init apartment ID
     g_module.RegisterManagedApartment(&gt_dwApartmentNumber);
 
 // create the worker threads
     DWORD dw;
     HANDLE hthreads[NAPARTMENTS];
     for (size_t i = 0; i < NAPARTMENTS; i++)
         hthreads[i] = CreateThread(0, 0, AptThreadProc, 0, 0, &dw);
 
 //  register the multiplexor with the SCM once the subapartments
 //  have gotten around to registering
     g_multiplexor.WaitForInternalApartmentsToRegister();
     g_multiplexor.RegisterExternalApartment(CLSID_CoSleeper);
 
 // run a message pump
     MSG msg;
     while (GetMessage(&msg, 0, 0, 0))
         DispatchMessage(&msg);
 // cleanup
     g_multiplexor.RevokeExternalApartment();
     g_multiplexor.RevokeInternalApartments();
 
     g_module.RevokeManagedApartment(gt_dwApartmentNumber);
     CoUninitialize();
 // wait around for worker threads to fade away...
     WaitForMultipleObjects(NAPARTMENTS, hthreads, TRUE, INFINITE);
     for (size_t ii = 0; ii < NAPARTMENTS; ii++)
         CloseHandle(hthreads[ii]);
     
     return msg.wParam;
 }
 
 int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR lpszCmdParam, int)
 {// register our implementation
     TCHAR szModuleName[MAX_PATH];
     GetModuleFileName(0, szModuleName, sizeof(szModuleName));
     RegSetValue(HKEY_CLASSES_ROOT, 
                 __TEXT("CLSID")
                 __TEXT("\\{227AE722-0E31-11cf-93E8-00C04FDE43AE}")
                 __TEXT("\\LocalServer32"), 
                 REG_SZ, 
                 szModuleName, 
                 lstrlen(szModuleName));
 
 // register our type lib
     ITypeLib *ptl = 0;
     if (SUCCEEDED(LoadTypeLib(OLESTR("Sleep.tlb"), &ptl)))
         ptl->Release();
 
 // exit if only registration is required
     if (strstr(lpszCmdParam, "/REGSERVER"))
         return -1;
 
 // Do the work of the main OLE thread
     DWORD dwResult = MainThreadProc(0);
 
     return int(dwResult);
}

Figure 11   Form1.frm


 VERSION 4.00
 Begin VB.Form Form1 
    BorderStyle     =   4  'Fixed ToolWindow
    Caption         =   "Form1"
    ClientHeight    =   645
    ClientLeft      =   2685
    ClientTop       =   2760
    ClientWidth     =   4080
    ControlBox      =   0   'False
    Height          =   1050
    Left            =   2625
    LinkTopic       =   "Form1"
    MaxButton       =   0   'False
    MinButton       =   0   'False
    ScaleHeight     =   645
    ScaleWidth      =   4080
    ShowInTaskbar   =   0   'False
    Top             =   2415
    Width           =   4200
    Begin VB.CommandButton ExitButton 
       Caption         =   "E&xit"
       Height          =   375
       Left            =   2760
       TabIndex        =   2
       Top             =   120
       Width           =   1215
    End
    Begin VB.CommandButton Sleep 
       Caption         =   "Sleep"
       Height          =   375
       Left            =   1440
       TabIndex        =   1
       Top             =   120
       Width           =   1215
    End
    Begin VB.TextBox Delay 
       Height          =   375
       Left            =   120
       TabIndex        =   0
       Text            =   "1000"
       Top             =   120
       Width           =   1215
    End
 End
 Attribute VB_Name = "Form1"
 Attribute VB_Creatable = False
 Attribute VB_Exposed = False
 Option Explicit
 ' this form assumes you have added the Sleep
 ' type library to the references for the project
 Dim sleeper As ISleep
 
 Private Sub ExitButton_Click()
 ' release the sleeper object
   Set sleeper = Nothing
   Unload Form1
 End Sub
 
 Private Sub Form_Load()
 ' this is VB for CoCreateInstance
   Set sleeper = New CoSleeper
   Form1.Caption = "SleepClient - Apartment " & sleeper.ApartmentNumber & "(tid = " & sleeper.ThreadID & ")"
 End Sub
 
 Private Sub Sleep_Click()
   Dim dactual As Long
   Sleep.Enabled = False
   Delay.Enabled = False
   dactual = sleeper.Sleep(Delay)
   MsgBox "I actually slept for " & dactual & " msecs."
   Sleep.Enabled = True
   Delay.Enabled = True
End Sub