Figure 7    Implementing Blind Delegation

BlindDel.h


 ////////////////////////////////////////////////////////////
 // blinddel.h - 1999, MSJ
 //
 // Generic blind delegation code
 //
 // If this code works, it was written by Don Box.
 // If it doesn't, it was written by Paul DiLascia
 //
 
 #ifndef _BLIND_DELEGATOR_H
 #define _BLIND_DELEGATOR_H
 
 enum { MAX_VTABLE_ENTRIES  = 1024 };
 typedef void (_stdcall *VTBL_ENTRY)();
 typedef VTBL_ENTRY VTABLE[MAX_VTABLE_ENTRIES];
 
 extern VTABLE g_bdvtbl;
 
 struct BlindDelegator
 {
     VTABLE                 *m_pVTable;
     IUnknown               *m_pUnkRealItf;
     IUnknown               *m_pCtlUnk;
     LONG                    m_cRef;
 
     static inline BlindDelegator *This(void *pvThis) 
     { 
         return (BlindDelegator*)(LPBYTE(pvThis) - offsetof(BlindDelegator, 
                                                            m_pVTable)); 
     }
 
     static HRESULT STDMETHODCALLTYPE QueryInterface(IUnknown *pvThis, REFIID 
                                                     riid, void **ppv)
     {
         return This(pvThis)->m_pCtlUnk->QueryInterface(riid, ppv);
     }
 
     static ULONG STDMETHODCALLTYPE AddRef(IUnknown *pvThis)
     {
         return InterlockedIncrement(&This(pvThis)->m_cRef);
     }
 
     static ULONG STDMETHODCALLTYPE Release(IUnknown *pvThis)
     {
         BlindDelegator *pThis = This(pvThis);
         LONG res = InterlockedDecrement(&pThis->m_cRef);
         if (res == 0)
         {
             IUnknown *pCtlUnk = pThis->m_pCtlUnk;
             pThis->m_pUnkRealItf->Release();
             delete pThis;
             res = pCtlUnk->Release();
         }
         return res;
     }
 
     static HRESULT CreateDelegator(IUnknown *pCtlUnk, IUnknown *pUnkDelegatee, 
                                    REFIID riid, void **ppv)
     {
         IUnknown *pItf = 0;
         *ppv = 0;
         HRESULT hr = pUnkDelegatee->QueryInterface(riid, (void**)&pItf);
         if (SUCCEEDED(hr))
         {
             BlindDelegator *pDel = new BlindDelegator;
             if (pDel)
             {
                 pDel->m_cRef = 1;
                 (pDel->m_pCtlUnk = pCtlUnk)->AddRef();
                 pDel->m_pUnkRealItf = pItf;
                 pDel->m_pVTable = &g_bdvtbl;
                 *ppv = LPBYTE(pDel) + offsetof(BlindDelegator, m_pVTable);
             }
             else
             {
                 pItf->Release();
                 hr = E_OUTOFMEMORY;
             }
 
         }
         return hr;
     }
 };
 
 #endif
BlindDel.cpp

 ////////////////////////////////////////////////////////////
 // blinddel.cpp - 1999, MSJ
 //
 // Generic blind delegation code
 //
 
 #include <stdafx.h>
 #include "blinddel.h"
 
 __declspec(naked) void Thunk()
 {
     _asm
     {
 // move BD::this into eax
         mov eax, DWORD PTR [esp + 4]
 // move BD::this->m_pUnkRealItf into eax
         mov eax, DWORD PTR [eax + 4]
 // move eax into "this" position on stack
         mov [esp + 4], eax
 // move base of real vtable into eax
         mov eax, DWORD PTR [eax]
 // add offset for current method
         add eax, ecx 
 // jmp to the actual function
         jmp DWORD PTR [eax]
     }
 }
 
 enum { SZPFN = sizeof(void (*)()) };
 
 #define METHODTHUNK(n) void __declspec(naked) methodthunk##n() {
  _asm { mov ecx,\ (n * SZPFN) } _asm { jmp Thunk } }
 
 #define METHODTHUNK10(n10) \
     METHODTHUNK(n10##0) \
     METHODTHUNK(n10##1) \
     METHODTHUNK(n10##2) \
     METHODTHUNK(n10##3) \
     METHODTHUNK(n10##4) \
     METHODTHUNK(n10##5) \
     METHODTHUNK(n10##6) \
     METHODTHUNK(n10##7) \
     METHODTHUNK(n10##8) \
     METHODTHUNK(n10##9) \
 
 #define METHODTHUNK100(n100) \
     METHODTHUNK10(n100##0) \
     METHODTHUNK10(n100##1) \
     METHODTHUNK10(n100##2) \
     METHODTHUNK10(n100##3) \
     METHODTHUNK10(n100##4) \
     METHODTHUNK10(n100##5) \
     METHODTHUNK10(n100##6) \
     METHODTHUNK10(n100##7) \
     METHODTHUNK10(n100##8) \
     METHODTHUNK10(n100##9) \
 
 
 METHODTHUNK(3)
 METHODTHUNK(4)
 METHODTHUNK(5)
 METHODTHUNK(6)
 METHODTHUNK(7)
 METHODTHUNK(8)
 METHODTHUNK(9)
 METHODTHUNK10(1)
 METHODTHUNK10(2)
 METHODTHUNK10(3)
 METHODTHUNK10(4)
 METHODTHUNK10(5)
 METHODTHUNK10(6)
 METHODTHUNK10(7)
 METHODTHUNK10(8)
 METHODTHUNK10(9)
 METHODTHUNK100(1)
 METHODTHUNK100(2)
 METHODTHUNK100(3)
 METHODTHUNK100(4)
 METHODTHUNK100(5)
 METHODTHUNK100(6)
 METHODTHUNK100(7)
 METHODTHUNK100(8)
 METHODTHUNK100(9)
 METHODTHUNK10(100)
 METHODTHUNK10(101)
 METHODTHUNK(1020)
 METHODTHUNK(1021)
 METHODTHUNK(1022)
 METHODTHUNK(1023)
 
 #define UMTHUNK(n) reinterpret_cast<VTBL_ENTRY>(methodthunk##n), 
 
 #define UMTHUNK10(n) \
     UMTHUNK(n##0) \
     UMTHUNK(n##1) \
     UMTHUNK(n##2) \
     UMTHUNK(n##3) \
     UMTHUNK(n##4) \
     UMTHUNK(n##5) \
     UMTHUNK(n##6) \
     UMTHUNK(n##7) \
     UMTHUNK(n##8) \
     UMTHUNK(n##9) \
 
 #define UMTHUNK100(n) \
     UMTHUNK10(n##0) \
     UMTHUNK10(n##1) \
     UMTHUNK10(n##2) \
     UMTHUNK10(n##3) \
     UMTHUNK10(n##4) \
     UMTHUNK10(n##5) \
     UMTHUNK10(n##6) \
     UMTHUNK10(n##7) \
     UMTHUNK10(n##8) \
     UMTHUNK10(n##9) \
 
 VTABLE g_bdvtbl = {
     reinterpret_cast<VTBL_ENTRY>(BlindDelegator::QueryInterface),
     reinterpret_cast<VTBL_ENTRY>(BlindDelegator::AddRef),
     reinterpret_cast<VTBL_ENTRY>(BlindDelegator::Release),
     UMTHUNK(3)
     UMTHUNK(4)
     UMTHUNK(5)
     UMTHUNK(6)
     UMTHUNK(7)
     UMTHUNK(8)
     UMTHUNK(9)
     UMTHUNK10(1)
     UMTHUNK10(2)
     UMTHUNK10(3)
     UMTHUNK10(4)
     UMTHUNK10(5)
     UMTHUNK10(6)
     UMTHUNK10(7)
     UMTHUNK10(8)
     UMTHUNK10(9)
     UMTHUNK100(1)
     UMTHUNK100(2)
     UMTHUNK100(3)
     UMTHUNK100(4)
     UMTHUNK100(5)
     UMTHUNK100(6)
     UMTHUNK100(7)
     UMTHUNK100(8)
     UMTHUNK100(9)
     UMTHUNK10(100)
     UMTHUNK10(101)
     UMTHUNK(1020)
     UMTHUNK(1021)
     UMTHUNK(1022)
     UMTHUNK(1023)
 };

Figure 9    MBV Delegator

MBVDelegator.h


 ////////////////////////////////////////////////////////////
 // MBVDelegator.h - 1999, MSJ
 //
 // Implementation of MBVDelegator
 //
 #ifndef __MBVDELEGATOR_H_
 #define __MBVDELEGATOR_H_
 #include "resource.h"       // main symbols
 #include "blinddel.h"
 
 /////////////////////////////////////////////////////////////////////////////
 // CMBVDelegator
 class ATL_NO_VTABLE CMBVDelegator : 
     public CComObjectRootEx<CComMultiThreadModel>,
     public CComCoClass<CMBVDelegator, &CLSID_MBVDelegator>,
     public IMarshal
 {
 public:
     CComPtr<IPersistStream>     m_ppsDelegatee;
     CComPtr<IUnknown>        m_pUnkFTM;
     CComPtr<IStream>            m_pStmCachedRead;
     DWORD                       m_cbMax;
     CMBVDelegator() : m_cbMax(0) {  }
 
 // called from internalQI to tear off a new blind delegator
     static HRESULT WINAPI _BlindDelegate(void* pvThis, REFIID riid, void** ppv, 
                                          DWORD dw)
     {
         CMBVDelegator *pThis = reinterpret_cast<CMBVDelegator*>(pvThis);
         return BlindDelegator::CreateDelegator(static_cast<IMarshal*>(pThis), 
             pThis->m_ppsDelegatee, riid, ppv);
     }
 
 DECLARE_REGISTRY_RESOURCEID(IDR_MBVDELEGATOR)
 DECLARE_PROTECT_FINAL_CONSTRUCT()
 DECLARE_GET_CONTROLLING_UNKNOWN()
 
 BEGIN_COM_MAP(CMBVDelegator)
     COM_INTERFACE_ENTRY(IMarshal)
     COM_INTERFACE_ENTRY_FUNC_BLIND(0, _BlindDelegate)
 END_COM_MAP()
 
 // IMarshal
 public:
     STDMETHODIMP GetUnmarshalClass(REFIID riid, void *pv, DWORD dwDestContext,
                                    void *pvDestContext, DWORD mshlflags,
                                    CLSID *pCid);
     STDMETHODIMP GetMarshalSizeMax(REFIID riid, void *pv, DWORD dwDestContext,
                                    void *pvDestContext, DWORD mshlflags,
                                    DWORD *pSize);
     STDMETHODIMP MarshalInterface(IStream *pStm, REFIID riid, void *pv,
                                   DWORD dwDestContext, void *pvDestContext,
                                   DWORD mshlflags);
     STDMETHODIMP UnmarshalInterface(IStream *pStm, REFIID riid, void **ppv);
     STDMETHODIMP ReleaseMarshalData(IStream *pStm);
     STDMETHODIMP DisconnectObject(DWORD dwReserved);
 
 };
 
 #endif //__MBVDELEGATOR_H_
MBVDelegator.cpp

 ////////////////////////////////////////////////////////////
 // MBVDelegator.cpp - 1999, MSJ
 //
 // Implementation of MBVDelegator
 //
 
 #include "stdafx.h"
 #include "Mbvref.h"
 #include "MBVDelegator.h"
 
 /////////////////////////////////////////////////////////////////////////////
 // CMBVDelegator
 struct MBV_HEADER
 {
     GUID                guidSignature; // == CLSID_MBVDelgator
     CLSID               clsid; // == GUID_NULL -> FTM
     unsigned __int64    cb;
 };
 
 STDMETHODIMP CMBVDelegator::GetUnmarshalClass(REFIID riid, void *pv, 
                                              DWORD dwDestContext,
                                              void *pvDestContext, 
                                              DWORD mshlflags, CLSID *pCid)
 {
     HRESULT hr = S_OK;
     if (dwDestContext == MSHCTX_INPROC && m_pUnkFTM)
     {
         CComPtr<IMarshal> pMsh;
         HRESULT hr = m_pUnkFTM->QueryInterface(&pMsh);
         if (SUCCEEDED(hr))
             hr = pMsh->GetUnmarshalClass(riid, pv, dwDestContext, pvDestContext, 
                                          mshlflags, pCid);
     }
     else
         *pCid = __uuidof(MBVDelegator);
     return hr;
 }
 
 STDMETHODIMP CMBVDelegator::GetMarshalSizeMax(REFIID riid, void *pv, 
                                              DWORD dwDestContext,
                                              void *pvDestContext, 
                                              DWORD mshlflags, DWORD *pSize)
 {
     HRESULT hr = S_OK;
     if (dwDestContext == MSHCTX_INPROC && m_pUnkFTM)
     {
         CComPtr<IMarshal> pMsh;
         HRESULT hr = m_pUnkFTM->QueryInterface(&pMsh);
         if (SUCCEEDED(hr))
             hr = pMsh->GetMarshalSizeMax(riid, pv, dwDestContext, pvDestContext,
                                          mshlflags, pSize);
     }
     else
     {
         __int64 cbMax;
         HRESULT hr = m_ppsDelegatee->GetSizeMax((ULARGE_INTEGER*)&cbMax);
         if (SUCCEEDED(hr))
             *pSize = DWORD(cbMax) + sizeof(MBV_HEADER);
         else
         {
             hr = S_OK;
             if (!m_pStmCachedRead)
             {
                 hr = CreateStreamOnHGlobal(0, TRUE, &m_pStmCachedRead);
                 if (SUCCEEDED(hr))
                 {
                     hr = m_ppsDelegatee->Save(m_pStmCachedRead, FALSE);
                     if (SUCCEEDED(hr))
                     {
                         LARGE_INTEGER liZero = { 0 };
                         hr = m_pStmCachedRead->Seek(liZero, STREAM_SEEK_CUR, 
                                                     (ULARGE_INTEGER*)&cbMax);
                         m_pStmCachedRead->Seek(liZero, STREAM_SEEK_SET, 0);
                         m_cbMax = DWORD(cbMax);
                         *pSize = m_cbMax + sizeof(MBV_HEADER);
                         
                     }
                     else
                     {
                         if (hr == 0x800a02e0)
                             OutputDebugString(__TEXT(
                                "error: MBVDelegator: Tried"       
                                " to marshal object prior to" 
                               " IPersistStreamInit::InitNew was called.\n"));
                     }
                 }
             }
             else
                 *pSize = m_cbMax + sizeof(MBV_HEADER);
             
         }
     }
     return hr;    
 }
 
 STDMETHODIMP CMBVDelegator::MarshalInterface(IStream *pStm, REFIID riid, 
                                             void *pv, DWORD dwDestContext, 
                                             void *pvDestContext, DWORD mshlflags)
 {
     HRESULT hr = S_OK;
     if (dwDestContext == MSHCTX_INPROC && m_pUnkFTM)
     {
         CComPtr<IMarshal> pMsh;
         HRESULT hr = m_pUnkFTM->QueryInterface(&pMsh);
         if (SUCCEEDED(hr))
             hr = pMsh->MarshalInterface(pStm, riid, pv, dwDestContext, 
                                         pvDestContext, mshlflags);
     }
     else
     {
         CLSID clsidDelegatee;
         HRESULT hr = m_ppsDelegatee->GetClassID(&clsidDelegatee);
         if (SUCCEEDED(hr))
         {
             CComPtr<IStream> pStmClone;
             hr = pStm->Clone(&pStmClone);
             if (SUCCEEDED(hr))
             {
                 MBV_HEADER header;
                 header.guidSignature = __uuidof(MBVDelegator);
                 header.clsid = clsidDelegatee;
                 hr = pStm->Write(&header, sizeof(header), 0);
                 if (SUCCEEDED(hr))
                 {
                     if (!m_pStmCachedRead)
                     {
                         hr = m_ppsDelegatee->Save(pStm, FALSE);
                         if (hr == 0x800a02e0)
                             OutputDebugString(__TEXT( "error: MBVDelegator:"
                               " Tried to marshal object prior to"
                               " IPersistStreamInit::InitNew was called.\n"));
                     }
                     else
                     {
                         ULARGE_INTEGER li;
                         li.QuadPart = m_cbMax;
                         hr = m_pStmCachedRead->CopyTo(pStm, li, 0, 0);
                         m_pStmCachedRead.Release();
                     }
                 }
                 // rewrite header with valid cb
                 unsigned __int64 start, end;
                 LARGE_INTEGER liZero = { 0 };
                 hr = pStm->Seek(liZero, STREAM_SEEK_CUR, (ULARGE_INTEGER*)&end);
                 if (SUCCEEDED(hr))
                     hr = pStmClone->Seek(liZero, STREAM_SEEK_CUR, 
                                          (ULARGE_INTEGER*)&start);
                 if (SUCCEEDED(hr))
                 {
                     header.cb = end - start - sizeof(MBV_HEADER);
                     hr = pStmClone->Write(&header, sizeof(header), 0);
                 }
             }
         }
     }
     return hr;
 }
 
 extern HRESULT IsApartmentNeutral(IUnknown *pUnk, bool *pbFTM);
 extern HRESULT GetFTMCLSID(CLSID *pclsid);
 
 STDMETHODIMP 
 CMBVDelegator::UnmarshalInterface(IStream *pStm, REFIID riid, void **ppv)
 {
     *ppv = 0;
     
     MBV_HEADER header;
     HRESULT hr = pStm->Read(&header, sizeof(header), 0);
     if (hr == S_OK)
     {
         CComPtr<IUnknown> pUnkDelegatee;
         hr = CoCreateInstance(header.clsid, 0, CLSCTX_ALL, 
                               __uuidof(pUnkDelegatee), (void**)&pUnkDelegatee);
         if (SUCCEEDED(hr))
             if (FAILED(hr = pUnkDelegatee->QueryInterface(&m_ppsDelegatee)))
                 hr = pUnkDelegatee->QueryInterface(__uuidof(IPersistStreamInit), 
                                                    (void**)&m_ppsDelegatee);
             
             if (SUCCEEDED(hr))
             {
                 hr = m_ppsDelegatee->Load(pStm);
                 if (SUCCEEDED(hr))
                 {
                     bool bFTM;
                     hr = IsApartmentNeutral(m_ppsDelegatee, &bFTM);
                     if (SUCCEEDED(hr))
                     {
                         if (bFTM)
                             hr = CoCreateFreeThreadedMarshaler(
                                     static_cast<IMarshal* (this),
                                      &m_pUnkFTM); 
                            if(SUCCEEDED(hr))
                                hr=GetControllingUnknown()->QueryInterface(
                                    riid, ppv);
                     }
                 }
             }
     }
     else
         hr = RPC_E_CLIENT_CANTUNMARSHAL_DATA;
     return hr;
 }
 
 STDMETHODIMP CMBVDelegator::ReleaseMarshalData(IStream *pStm)
 {
     MBV_HEADER header = { 0 };
     HRESULT hr = pStm->Read(&header, sizeof(header), 0);
     if (SUCCEEDED(hr) && header.guidSignature == __uuidof(MBVDelegator))
         hr = pStm->Seek(*(LARGE_INTEGER*)&header.cb, STREAM_SEEK_CUR, 0);
     return hr;
 }
 
 STDMETHODIMP CMBVDelegator::DisconnectObject(DWORD dwReserved)
 {
     return S_OK;
}