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(>_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(>_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