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