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