Figure 2   Imarshal


 interface IMarshal : IUnknown {
 // what is the clsid of the handler ?
   HRESULT GetUnmarshalClass(REFIID riid,
                             void *pvObject,
                             DWORD dwDestContext,
                             void *pvDestContext,
                             DWORD mshlflags,
                             CLSID *pClsid);
         
 // how big does the marshaling packet have to be?
   HRESULT GetMarshalSizeMax(REFIID riid,
                             void *pvObject,
                             DWORD dwDestContext,
                             void *pvDestContext,
                             DWORD mshlflags,
                             DWORD *pSize);
         
 // fill the marshaling packet
   HRESULT MarshalInterface( IStream *pStm,
                             REFIID riid,
                             void *pvObject,
                             DWORD dwDestContext,
                             void *pvDestContext,
                             DWORD mshlflags);
         
 // initialize the handler from marshaling packet 
   HRESULT UnmarshalInterface(IStream *pStm,
                              REFIID riid,
                              void **ppvObject);
         
 // The marshaling packet is no longer needed
   HRESULT ReleaseMarshalData(IStream *pStm);
 
 // the object called CoDisconnectObject to sever
 // any outstanding connections
   HRESULT DisconnectObject(DWORD dwReserved);
 }


Figure 3   Using Packets and Handlers


 HGLOBAL *CreatePacket(IUnknown *pUnkSource,
                       REFIID riidInitialInterface)
 {
     HGLOBAL result = 0;
 // grab the IMarshal interface
     IMarshal *pmsh;
     pUnkSource->QueryInterface(IID_IMarshal,
                        (void**)&pmsh);
 
 // allocate the memory for the packet and 
 // create an IStream interface to it
     ULONG cbPacketSize;
     pmsh->GetMarshalSizeMax(riidInitialInterface,
                             pUnkSource,
                             MSHCTX_LOCAL,
                             0,
                             MSHLFLAGS_NORMAL,
                             &cbPacketSize);
 
     result = GlobalAlloc(GHND, cbPacketSize);
 
     IStream *pstm = 0;
     CreateStreamOnHGlobal(result, FALSE, &pstm);
 
 // write out the handler's CLSID and any data that
 // will be needed to connect it to the object
     CLSID clsid;
     pmsh->GetUnmarshalClass(riidInitialInterface,
                             pUnkSource,
                             MSHCTX_LOCAL,
                             0,
                             MSHLFLAGS_NORMAL,
                             &clsid);
 
     WriteClassStm(pstm, clsid);
     pmsh->MarshalInterface( pstm,
                             riidInitialInterface,
                             pUnkSource,
                             MSHCTX_LOCAL,
                             0,
                             MSHLFLAGS_NORMAL);
     
     pstm->Release();
     pmsh->Release();
     return result;
 }              
 
 
 void CreateHandler(HGLOBAL hgpacket,
                    REFIID riidInitialInterface,
                    void **ppvObj)
 {
 // create an IStream interface to the packet
     IStream *pstm = 0;
     CreateStreamOnHGlobal(hgpacket, FALSE, &pstm);
 
 // read the CLSID for the handler and instantiate
     CLSID clsid;
     ReadClassStm(pstm, &clsid);
     IMarshal *pmsh = 0;
     CoCreateInstance(clsid, 0, 
                      CLSCTX_INPROC_HANDLER,
                      IID_IMarshal, (void**)&pmsh);
 
 // connect/initialize the handler
     IStream *pstmClone;
     pstm->Clone(&pstmClone); // keep our place
     pmsh->UnmarshalInterface(pstm, 
                              riidInitialInterface,
                              ppvObj);
 
 // release the marshaling packet
     pmsh->ReleaseMarshalData(pstmClone);
     pstm->Release();
     pstmClone->Release();
     pmsh->Release();
     GlobalFree(0, hgpacket);
 }


Figure 6   Generic Shared Object

SHAREDOB.H


 /////////////////////////////////////////////////////////////
 //
 // SharedObj.h - 1996, Don Box
 //
 
 #ifndef _SHAREDOBJ_H
 #define _SHAREDOBJ_H
 
 // we need the placement operator
 #include <new.h>
 
 // CoSharedObjectBase is a non-template base class that implements 
 // most of the custom marshaling code to share a memory section in 
 // a thread-safe fashion
 
 class CoSharedObjectBase : private IMarshal {
 // handle to mutex that protects the shared
 // data members
     HANDLE m_hmutex;
 
 // handle to section object that contains 
 // shared data members
     HANDLE m_hsection;
 
 // pointer to shared data member(s)
     void *m_pvData;
 
 // unique GUID that "names" the 2 shared Win32
 // kernel objects
     GUID m_guid;
 
 // normal COM refcount
     ULONG m_cRef;
 
 // helper function to attach to section object
     BOOL AttachToSection(DWORD dwTimeOut = INFINITE);
 
     struct SECTIONHEADER;
     SECTIONHEADER *GetSectionHeader();
     void *GetSectionData();
 
 // Virtual upcalls that allow derived client to construct/destroy 
 // shared data members
     virtual BOOL OnInitializeSection(void *pv) = 0;
     virtual void OnDestroySection(void *pv) = 0;
 
 // Virtual upcalls that are used to find the size of the shared 
 // data members and the CLSID of this class
     virtual DWORD OnGetSize() = 0;
     virtual const CLSID& OnGetCLSID() = 0;
 
 // IMarshal members
     STDMETHODIMP GetUnmarshalClass( REFIID riid,      
                                     void * pv,      
                                     DWORD dwDestCtx,
                                     void * pvDestCtx,
                                     DWORD mshlflags,
                                     CLSID * pclsid);
 
     STDMETHODIMP GetMarshalSizeMax( REFIID riid,      
                                     void * pv,      
                                     DWORD dwDestCtx,
                                     void * pvDestCtx,
                                     DWORD mshlflags,
                                     ULONG *pcb);
 
     STDMETHODIMP MarshalInterface(  IStream *pStm,
                                     REFIID riid,
                                     void *pv,
                                     DWORD dwDestCtx,
                                     void *pvDestCtx,
                                     DWORD mshlflags);
 
     STDMETHODIMP UnmarshalInterface(IStream * pStm,
                                     REFIID riid,
                                     void ** ppv);
 
     STDMETHODIMP ReleaseMarshalData(IStream * pStm);
 
     STDMETHODIMP DisconnectObject(DWORD dwReserved);
 protected:
 // IUnknown members (protected to allow forwarding)
     STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
     STDMETHODIMP_(ULONG) AddRef();
     STDMETHODIMP_(ULONG) Release();
 
 // Lock/Unlock the shared data
     void *AccessSharedData(DWORD dwTimeOut = INFINITE);
     void  ReleaseSharedData();
 
     CoSharedObjectBase();
     virtual ~CoSharedObjectBase();
 };
 
 
 // CoSharedObject is a template class that implements a handler
 // that accesses a shared data structure (SharedState) and implements
 // one COM interface (beyond IMarshal)
 
 template <  class SharedState,     // the shared data
                   const CLSID *pclsid,   // this CLSID
             interface IUser,       // Primary Interface
                   const IID* PIID_IUser  // Primary IID
              >
 class CoSharedObject :  private CoSharedObjectBase, 
                         public  IUser {
 private:
 // default implementation uses placement version
 // of new to construct the object "in place"
     virtual BOOL OnInitializeSection(void *pv) {
         new (pv) SharedState;
         return TRUE;
     }
 
 // default implementation calls destructor explicitly
 // to destruct the object "in place"
     virtual void OnDestroySection(void *pv)  {
         ((SharedState*)pv)->~SharedState();
     }
 
 // default implementation returns the size of the 
 // data members without chasing pointer members
     virtual DWORD OnGetSize() {
         return sizeof(SharedState);
     }
     
 // simply return the template paramter
     virtual const CLSID& OnGetCLSID() {
         return *pclsid;
     }
     
 protected:
 // SharedThis is a helper class that aquires the mutex in
 // an exception-safe fashion. Declare an instance of SharedThis
 // at the beginning of each member function of your derived class
 // to access the shared data members of your object
 
     class SharedThis {
         CoSharedObject *m_phandler;
         SharedState *m_pthis;
     public:
         SharedThis(CoSharedObject *pt, DWORD dwTimeOut = INFINITE) 
         :   m_phandler(pt),
             m_pthis(0)
         {
             m_pthis = (SharedState*)pt->AccessSharedData(dwTimeOut);
         }
 
         ~SharedThis()
         {
                   if (m_pthis)
                         m_phandler->ReleaseSharedData();
         }
 
         operator SharedState * () { return m_pthis; }
         SharedState * operator -> () { return m_pthis; }
     };
     friend class SharedThis;
 
 public:
 
 // hook QI to expose the new interface
     STDMETHODIMP QueryInterface(REFIID riid, void**ppv) {
         if (riid = = *PIID_IUser) {
             LPUNKNOWN(*ppv = (IUser*)this)->AddRef();
             return S_OK;
         }
         return CoSharedObjectBase::QueryInterface(riid, ppv);
     }
     
 // use default AddRef and Release
     STDMETHODIMP_(ULONG) AddRef() {
         return CoSharedObjectBase::AddRef();
     }
 
     STDMETHODIMP_(ULONG) Release() {
         return CoSharedObjectBase::Release();
     }
 
 };
 
 #endif

SHAREDOB.CPP


 /////////////////////////////////////////////////////////////
 //
 // SharedObj.cpp - 1996, Don Box
 //
 
 #include <windows.h>
 #include "sharedobj.h"
 
 // standard module lifetime functions (defined elsewhere)
 extern void SvcLock();
 extern void SvcUnlock();
 
 // header for shared memory section
 struct CoSharedObjectBase::SECTIONHEADER {
 // shared ref count
     DWORD m_cRef;
 // padding
     DWORD m_reserved;
 };
 
 // the header resides at the beginning of the section
 inline CoSharedObjectBase::SECTIONHEADER *
 CoSharedObjectBase::GetSectionHeader() {
     return (SECTIONHEADER*)m_pvData;
 }
 
 // the payload resides immediately after the header, so use 
 // the magic of pointer arithmetic to find its address
 inline void *
 CoSharedObjectBase::GetSectionData() {
     return GetSectionHeader() + 1;
 }
 
 // initialize data members to null and lock the server module
 CoSharedObjectBase::CoSharedObjectBase()
 :   m_hmutex(0), m_hsection(0), m_pvData(0), m_cRef(0)
 {
     SvcLock();
 }
 
 // close all handles and unlock the server module
 CoSharedObjectBase::~CoSharedObjectBase() {
     if (m_hsection) {
         UnmapViewOfFile(m_pvData);
         CloseHandle(m_hsection);
     }
 
     if (m_hmutex)
         CloseHandle(m_hmutex);
     SvcUnlock();
 }
 
 
 // helper function to create or open section object and mutex
 BOOL 
 CoSharedObjectBase::AttachToSection(DWORD dwTimeOut)
 {
 // synthesize section and mutex name based on guid value,(e.g., 
 // "{12345678-0000-0000-0000-DEAD00000000}_MTX" for the mutex
 // "{12345678-0000-0000-0000-DEAD00000000}_SCT" and for the section
 
     TCHAR szSectionName[128];
     TCHAR szMutexName[128];
     OLECHAR szGUID[128];
 
 // m_guid acts as the object ID in this usage
     StringFromGUID2(m_guid, szGUID, sizeof(szGUID));
 
 #ifdef UNICODE
     lstrcpy(szSectionName, szGUID);
 #else
     wcstombs(szSectionName, szGUID, sizeof(szSectionName));
 #endif
 
     lstrcpy(szMutexName, szSectionName);
     lstrcat(szMutexName, TEXT("_MTX"));
     lstrcat(szSectionName, TEXT("_SCT"));
 
 // Create/open the mutex, ascertain "firstness" and grab the mutex
     m_hmutex = CreateMutex(0, FALSE, szMutexName);
     BOOL bFirst = GetLastError() != ERROR_ALREADY_EXISTS;
     WaitForSingleObject(m_hmutex, dwTimeOut);
 
 // create/open a section big enough to hold shared state and 
 // header, then map it into address space
     m_hsection = CreateFileMapping(HANDLE(0xFFFFFFFF), 0, PAGE_READWRITE, 0,
                                    sizeof(SECTIONHEADER) + OnGetSize(),
                                    szSectionName);
 
     m_pvData = MapViewOfFile(m_hsection, FILE_MAP_ALL_ACCESS, 0,0,0);
         
        if (bFirst)
     {
 // set shared ref count to one and initialize payload of section via
 // the virtual function OnInitializeSection
         GetSectionHeader()->m_cRef = 1;
         OnInitializeSection(GetSectionData());
     }
     else
     {
 // simply bump the shared ref count
         GetSectionHeader()->m_cRef++;
     }
 
     ReleaseMutex(m_hmutex);
     return TRUE;
 }
 
 
 // IUnknown members
 STDMETHODIMP 
 CoSharedObjectBase::QueryInterface(REFIID riid, void **ppv)
 {
     if (riid = = IID_IUnknown || riid = = IID_IMarshal)
         LPUNKNOWN(*ppv = (IMarshal*)this)->AddRef();
     else
         *ppv = 0;
     return *ppv ? S_OK : E_NOINTERFACE;
 }
 
 STDMETHODIMP_(ULONG) 
 CoSharedObjectBase::AddRef()
 {
     return ++m_cRef;
 }
 
 STDMETHODIMP_(ULONG) 
 CoSharedObjectBase::Release()
 {
     ULONG result = --m_cRef;
     if (result = = 0)
     {
 // We must detach from section prior to destruction
 // in order to get the appropriate virtual function
 // called (we can't call OnDestroySection in our destructor)
 
         WaitForSingleObject(m_hmutex, INFINITE);
         if (--GetSectionHeader()->m_cRef = = 0)
             OnDestroySection(GetSectionData()); 
         ReleaseMutex(m_hmutex);
 
         delete this;
     }
     return result;
 }
 
 // IMarshal members
 
 // GetUnmarshalClass uses the virtual upcall OnGetCLSID to
 // ascertain the classname of the handler. Marshaling contexts
 // that do not support shared memory simply fail
 
 STDMETHODIMP 
 CoSharedObjectBase::GetUnmarshalClass(  REFIID riid, void * pv, DWORD dwDestCtx,
                                         void * pvDestCtx, DWORD mshlflags,
                                         CLSID * pclsid)
 {
     SCODE result = E_FAIL;
     if (dwDestCtx = = MSHCTX_LOCAL ||
         dwDestCtx = = MSHCTX_INPROC)
     {
         *pclsid = OnGetCLSID();
         result = S_OK;
     }
     return result;
 }
 
 // Since we only send the object ID (m_guid) in the marshaling
 // packet, we hard code the size to sizeof(GUID) 
 
 STDMETHODIMP 
 CoSharedObjectBase::GetMarshalSizeMax( REFIID riid, void * pv, DWORD dwDestCtx,
                                         void * pvDestCtx, DWORD mshlflags,
                                         ULONG *pcb)
 {
     SCODE result = E_FAIL;
     if (dwDestCtx = = MSHCTX_LOCAL ||
         dwDestCtx = = MSHCTX_INPROC)
     {
         *pcb = sizeof(GUID);
         result = S_OK;
     }
     return result;
 }
 
 
 // We transmit the object ID (m_guid) through the stream
 // using WriteClassStm
 
 STDMETHODIMP 
 CoSharedObjectBase::MarshalInterface(  IStream *pStm, REFIID riid, void *pv,
                                         DWORD dwDestCtx, void *pvDestCtx,
                                         DWORD mshlflags)
 {
     SCODE result = E_FAIL;
     if (dwDestCtx = = MSHCTX_LOCAL ||
         dwDestCtx = = MSHCTX_INPROC)
     {
         WriteClassStm(pStm, m_guid);
         result = S_OK;
     }
     return result;
 }
 
 // We receive the object ID (m_guid) from the stream
 // using ReadClassStm and attach to the shared section
 // that the object ID names
 
 STDMETHODIMP 
 CoSharedObjectBase::UnmarshalInterface(IStream * pStm, REFIID riid, void ** ppv)
 {
     *ppv = 0;
     ReadClassStm(pStm, &m_guid);
     if (AttachToSection())
         return this->QueryInterface(riid, ppv);
     else
         return E_FAIL;
 }
 
 // since we allocate no new resources in our marshaling code,
 // there is no need to implement anything interesting 
 // in this function
 
 STDMETHODIMP 
 CoSharedObjectBase::ReleaseMarshalData(IStream * pStm)
 {
 // seek past our portion of the marshaling packet
     LARGE_INTEGER li;
     LISet32(li, sizeof(m_guid));
     pStm->Seek(li, STREAM_SEEK_CUR, 0);
     return S_OK;
 }
 
 // For the simple case, we do not want the handler to disconnect
 
 STDMETHODIMP 
 CoSharedObjectBase::DisconnectObject(DWORD dwReserved)
 {
     return S_OK;
 }
 
 
 // AccessSharedData allows derived clients to acquire the mutex. If the
 // section has not been mapped, then this is the first access to the object
 // and causes the object ID to be generated using CoCreateGuid
 // This function and AttachToSection perform most of the work of this class.
 
 void *
 CoSharedObjectBase::AccessSharedData(DWORD dwTimeOut)
 {
 // test for first access to first handler
     if (m_pvData = = 0)
     {
         CoCreateGuid(&m_guid);
         if (!AttachToSection(dwTimeOut))
             return 0;
     }
 
     if (WaitForSingleObject(m_hmutex, dwTimeOut) = = WAIT_OBJECT_0)
         return GetSectionData();
     else
         return 0;
 }
 
 // simply release the mutex
 
 void  
 CoSharedObjectBase::ReleaseSharedData()
 {
     ReleaseMutex(m_hmutex);
 }


Figure 8   CoSharedObject Demo

SHAREDPO.H


 /////////////////////////////////////////////////////////////
 //
 // SharedPoint.h - 1996 Don Box
 //
 
 #ifndef _SHAREDPOINT_H
 #define _SHAREDPOINT_H
 
 #include "SharedObj.h"
 #include "IPoint.h"
 
 
 // shared data members
 class PointRep {
     friend class CoSharedPoint;
     long m_x;
     long m_y;
     char m_sz[1024];
 public:
     PointRep() : m_x(100), m_y(200) { lstrcpyA(m_sz, "Some Other Model?"); }
     ~PointRep() { OutputDebugString("I am done!\n"); }
 };
 
 // handler class that is instantiated in each process
 class CoSharedPoint 
             : public CoSharedObject<PointRep,             // Shared state
                                 &CLSID_CoSharedPoint, // this CLSID
                                 IPointAndText,        // Primary interface
                                 &IID_IPointAndText>   // Primary IID
 {
 public:
 // IPointAndText members
     STDMETHODIMP Set(long x, long y);
     STDMETHODIMP Get(long FAR* px, long FAR* py);
     STDMETHODIMP SetText(LPCSTR lpsz);
     STDMETHODIMP GetText(LPSTR lpsz, int cb);
 };
 
 #endif

SHAREDPO.CPP


 /////////////////////////////////////////////////////////////
 //
 // SharedPoint.cpp - 1996, Don Box
 //
 
 #include <windows.h>
 #include "sharedpoint.h"
 
 STDMETHODIMP 
 CoSharedPoint::Set(long x, long y)
 {
     SharedThis pThis(this); // initialize locked pointer to shared state
     pThis->m_x = x;         // copy parameters to shared state
     pThis->m_y = y;
     return NOERROR;
 }
 
 STDMETHODIMP 
 CoSharedPoint::Get(long FAR* px, long FAR* py)
 {
     SharedThis pThis(this); // initialize locked pointer to shared state
     *px = pThis->m_x;       // copy parameters from shared state
     *py = pThis->m_y;
     return NOERROR;
 }
 
 STDMETHODIMP 
 CoSharedPoint::SetText(LPCSTR lpsz)
 {
     SharedThis pThis(this);
     if (strncpy(pThis->m_sz, lpsz, sizeof(pThis->m_sz)))
         return S_OK;
     else
         return E_INVALIDARG;
 }
 
 STDMETHODIMP 
 CoSharedPoint::GetText(LPSTR lpsz, int cb)
 {
     SharedThis pThis(this);
     if (strncpy(lpsz, pThis->m_sz, cb))
         return S_OK;
     else
         return E_INVALIDARG;
 }