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;
}