Figure 7 Adding an Envelope
// ClassObjectShim implementation
STDMETHODIMP ClassObjectShim::CreateInstance(IUnknown *pUnkOuter,
REFIID riid,
void **ppv)
{
*ppv = 0;
COSERVERINFO csi = { 0, OLESTR("SomeServer"), 0, 0 };
MULTI_QI mqi = { riid, 0, 0 };
hr = CoCreateInstanceEx(m_clsid, 0,
CLSCTX_REMOTE_SERVER,
&csi,
1, &mqi);
if (SUCCEEDED(hr))
{
IEnvelope *pe = 0;
hr = CoCreateInstance(CLSID_Envelope, 0, CLSCTX_INPROC_SERVER,
IID_IEnvelope, (void**) &pe);
if (SUCCEEDED(hr))
{
// put letter into envelope
hr = pe->put_Letter(reinterpret_cast<IUnknown*>(*ppv));
// release reference to letter, envelope holds it
(reinterpret_cast<IUnknown*>(*ppv))->Release();
// return a pointer to envelope instead
(*ppv = pe)->AddRef();
// release reference to envelope
pe->Release();
}
}
return hr;
}
Figure 10 ATL Envelope Implementation
class ATL_NO_VTABLE CEnvelope :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CEnvelope, &CLSID_Envelope>,
public IEnvelope,
public IMarshal
{
CComPtr<IUnknown> m_pUnk;
public:
CEnvelope() {}
virtual ~CEnvelope() {}
DECLARE_REGISTRY_RESOURCEID(IDR_ENVELOPE)
DECLARE_NOT_AGGREGATABLE(CEnvelope)
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(CEnvelope)
COM_INTERFACE_ENTRY(IEnvelope)
COM_INTERFACE_ENTRY(IMarshal)
END_COM_MAP()
public:
// IEnvelope
STDMETHODIMP CEnvelope::put_Letter(IUnknown *pUnk)
{
// store reference to letter
m_pUnk = pUnk;
return S_OK;
}
// IMarshal
STDMETHODIMP CEnvelope::GetUnmarshalClass(REFIID riid,
void *pv,
DWORD dwDestContext,
void *pvDestContext,
DWORD mshlflags,
CLSID *pCid)
{
// return Envelope CLSID to shield raw reference
// from CoCreateInstance[Ex]'s prying eyes
*pCid = CLSID_Envelope;
return S_OK;
}
STDMETHODIMP CEnvelope::GetMarshalSizeMax(REFIID riid,
void *pv,
DWORD dwDestContext,
void *pvDestContext,
DWORD mshlflags,
DWORD *pSize)
{
// get enough space to carry a standard
// marshal of letter reference
return CoGetMarshalSizeMax(pSize, riid, m_pUnk, dwDestContext,
pvDestContext, mshlflags);
}
STDMETHODIMP CEnvelope::MarshalInterface(IStream *pStm,
REFIID riid,
void *pv,
DWORD dwDestContext,
void *pvDestContext,
DWORD mshlflags)
{
// carry a standard marshal of letter
// reference as custom payload
return CoMarshalInterface(pStm, riid, m_pUnk, dwDestContext,
pvDestContext, mshlflags ) ;
}
STDMETHODIMP CEnvelope::UnmarshalInterface(IStream *pStm,
REFIID riid,
void **ppv)
{
// create proxy to letter and return it directly
// (open the envelope)
return CoUnmarshalInterface(pStm, riid, ppv);
}
STDMETHODIMP CEnvelope::ReleaseMarshalData(IStream *pStm)
{
// clean up the standard marshal if asked
return CoReleaseMarshalData(pStm);
}
STDMETHODIMP CEnvelope::DisconnectObject(DWORD dwReserved)
{
// nothing to disconnect
return S_OK;
}
};
Figure 11 Using an Envelope
// ClassObjectShim implementation
STDMETHODIMP ClassObjectShim::CreateInstance(IUnknown *pUnkOuter,
REFIID riid,
void **ppv)
{
*ppv = 0;
hr = g_pAlgorithm->CreateInstance(m_clsid, riid, ppv);
if (SUCCEEDED(hr))
{
IEnvelope *pe = 0;
hr = CoCreateInstance(CLSID_Envelope, 0, CLSCTX_INPROC_SERVER,
IID_IEnvelope, (void**) &pe);
if (SUCCEEDED(hr))
{
// put letter into envelope
hr = pe->put_Letter(reinterpret_cast<IUnknown*>(*ppv));
// release reference to letter, envelope holds it
(reinterpret_cast<IUnknown*>(*ppv))->Release();
// return a pointer to envelope instead
(*ppv = pe)->AddRef();
// release reference to envelope
pe->Release();
}
}
return hr;
}
Figure 12 Method Timing Channel Hook Implementation
// Hook.h
class Hook : public IChannelHook
{
public:
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
// IChannelHook
STDMETHODIMP_(void) ClientGetSize(REFGUID uExtent, REFIID riid,
ULONG *pDataSize);
STDMETHODIMP_(void) ClientFillBuffer(REFGUID uExtent, REFIID riid,
ULONG *pDataSize,
void *pDataBuffer);
STDMETHODIMP_(void) ClientNotify(REFGUID uExtent, REFIID riid,
ULONG cbDataSize, void *pDataBuffer,
DWORD lDataRep, HRESULT hrFault);
STDMETHODIMP_(void) ServerNotify(REFGUID uExtent, REFIID riid,
ULONG cbDataSize, void *pDataBuffer,
DWORD lDataRep);
STDMETHODIMP_(void) ServerGetSize(REFGUID uExtent, REFIID riid,
HRESULT hrFault, ULONG *pDataSize);
STDMETHODIMP_(void) ServerFillBuffer(REFGUID uExtent, REFIID riid,
ULONG *pDataSize,
void *pDataBuffer,
HRESULT hrFault);
};
// Hook.cpp
#pragma data_seg("Shared")
long g_nCount = 0;
long g_nTime = 0;
#pragma data_seg()
#pragma comment(linker, "/section:Shared,rws")
extern HANDLE g_hMutex;
// information about a particular call site
struct CALLINFO
{
time_t tStart;
GUID guidCausality;
CALLINFO *pNext;
};
extern DWORD g_dwTLSIndex;
extern HINSTANCE g_hInstance;
STDMETHODIMP
Hook::QueryInterface(REFIID riid, void **ppv)
{
if (riid == IID_IUnknown)
*ppv = static_cast<IUnknown*>(this);
else if (riid == IID_IUnknown)
*ppv = static_cast<IChannelHook*>(this);
else
return (*ppv = 0), E_NOINTERFACE;
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG)
Hook::AddRef(void)
{
return 2;
}
STDMETHODIMP_(ULONG)
Hook::Release(void)
{
return 1;
}
// called in client prior to making a call
STDMETHODIMP_(void)
Hook::ClientGetSize(REFGUID uExtent, REFIID riid, ULONG *pDataSize)
{
assert(uExtent == EXTENTID_MethodTime);
*pDataSize = 0;
return;
}
// called in client prior to making a call
STDMETHODIMP_(void)
Hook::ClientFillBuffer(REFGUID uExtent, REFIID riid, ULONG *pDataSize,
void *pDataBuffer)
{
assert(uExtent == EXTENTID_MethodTime);
*pDataSize = 0;
return;
}
// called in client just after a call completes
STDMETHODIMP_(void)
Hook::ClientNotify(REFGUID uExtent, REFIID riid, ULONG cbDataSize,
void *pDataBuffer, DWORD lDataRep, HRESULT hrFault)
{
assert(uExtent == EXTENTID_MethodTime);
return;
}
// called in server just prior to invoking a call
STDMETHODIMP_(void)
Hook::ServerNotify(REFGUID uExtent, REFIID riid, ULONG cbDataSize,
void *pDataBuffer, DWORD lDataRep)
{
assert(uExtent == EXTENTID_MethodTime);
assert(lDataRep == NDR_LOCAL_DATA_REPRESENTATION);
// allocate new callinfo
CALLINFO *pciNew = new CALLINFO;
assert(pciNew);
memset(pciNew, 0, sizeof(CALLINFO));
// record time
time(&pciNew->tStart);
// get access to causality
const SChannelHookCallInfo* pchci =
reinterpret_cast<const SChannelHookCallInfo*>(&riid);
// record causality
pciNew->guidCausality = pchci->uCausality;
// get existing stack of callinfos
CALLINFO *pciHead =
reinterpret_cast<CALLINFO*>(TlsGetValue(g_dwTLSIndex));
// push onto stack of callinfos
if (pciHead)
pciNew->pNext = pciHead;
// store stack of callinfos
TlsSetValue(g_dwTLSIndex, pciNew);
return;
}
// called in server just after invoking a call
STDMETHODIMP_(void)
Hook::ServerGetSize(REFGUID uExtent, REFIID riid, HRESULT hrFault,
ULONG *pDataSize)
{
assert(uExtent == EXTENTID_MethodTime);
*pDataSize = sizeof(BYTE);
return;
}
// called in server just after invoking a call
STDMETHODIMP_(void)
Hook::ServerFillBuffer(REFGUID uExtent, REFIID riid, ULONG *pDataSize,
void *pDataBuffer, HRESULT hrFault)
{
assert(uExtent == EXTENTID_MethodTime);
*pDataSize = sizeof(BYTE);
*(BYTE*)pDataBuffer = 0;
// get stack of callinfos
CALLINFO *pciHead =
reinterpret_cast<CALLINFO*>(TlsGetValue(g_dwTLSIndex));
// if there is a stack (there should be)
if (pciHead)
{
// get access to causality
const SChannelHookCallInfo* pchci =
reinterpret_cast<const SChannelHookCallInfo*>(&riid);
// look for duplicate causality in stack
CALLINFO *pciIndex = pciHead->pNext;
while (pciIndex != 0)
{
// break if duplicate causality found
if (pciIndex->guidCausality == pchci->uCausality)
break;
pciIndex = pciIndex->pNext;
}
// no duplicate causality
if (pciIndex == 0)
{
// update timing data
time_t tEnd;
time(&tEnd);
DWORD dw = WaitForSingleObject(g_hMutex, INFINITE);
g_nCount++;
g_nTime += (tEnd - pciHead->tStart);
ReleaseMutex(g_hMutex);
}
// store new stack of callinfos, popping head
TlsSetValue(g_dwTLSIndex, pciHead->pNext);
// cleanup head
delete pciHead;
}
return;
}
Figure 13 NewDllMain Entry Point
// MethodTimeHookPS.cpp
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason,
LPVOID lpReserved);
extern "C"
BOOL WINAPI NewDllMain(HINSTANCE hInstance,
DWORD dwReason,
LPVOID lpReserved)
{
static IUnknown *pUnk = 0;
switch(dwReason)
{
case DLL_PROCESS_ATTACH :
{
HRESULT hr = CoCreateInstance(CLSID_Loader, 0,
CLSCTX_INPROC_SERVER,
IID_IUnknown, (void**)&pUnk);
if (SUCCEEDED(hr)) pUnk->Release();
break;
}
case DLL_PROCESS_DETACH :
{
break;
}
}
return DllMain(hInstance, dwReason, lpReserved);
}
Figure 14 Proxy/Stub DLL Makefile
TestServerps.dll: dlldata.obj TestServer_p.obj TestServer_i.obj \
MethodTimeHookPS.obj
link /dll /out:TestServerps.dll /def:TestServerps.def \
/entry:NewDllMain MethodTimeHookPS.obj \
dlldata.obj TestServer_p.obj TestServer_i.obj \
mtxih.lib ole32.lib advapi32.lib mtx.lib mtxguid.lib \
kernel32.lib rpcndr.lib rpcns4.lib rpcrt4.lib \
oleaut32.lib uuid.lib
.c.obj:
cl /c /Ox /DWIN32 /D_WIN32_WINNT=0x0400 /DREGISTER_PROXY_DLL \
/MD \
$<
.cpp.obj:
cl /c /Ox /DWIN32 /D_WIN32_WINNT=0x0400 /DREGISTER_PROXY_DLL \
/MD \
$<
clean:
@del TestServerps.dll
@del TestServerps.lib
@del TestServerps.exp
@del dlldata.obj
@del TestServer_p.obj
@del TestServer_i.obj
@del MethodTimeHookPS.obj
Figure 15 Loader's GetAverageMethodTime Method
STDMETHODIMP CLoader::GetAverageMethodTime(long *pnAvg)
{
static time_t tStart = time(&tStart);
time_t tEnd;
if (tStart == time(&tEnd)) // for first interval
{
*pnAvg = 0;
return S_OK;
}
DWORD dw = WaitForSingleObject(g_hMutex, INFINITE);
// Average task-seconds per interval
*pnAvg = (((float) g_nTime) / (tEnd - tStart)) * 1000;
g_nTime = 0;
ReleaseMutex(g_hMutex);
tStart = tEnd;
return S_OK;
}
Figure 16 MethodTimeMonitor Thread Function
void _cdecl MethodTimeMonitor(void *pv)
{
CMethodTiming *pmt = reinterpret_cast<CMethodTiming*>(pv);
CMethodTiming::HostTimeInfo *prghti = pmt->m_rghti;
HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hr)) return;
while (WaitForSingleObject(pmt->m_hStopping, 500) == WAIT_TIMEOUT)
{
long nLowestIndex = 0;
long nLowestValue = LONG_MAX;
pmt->Lock();
for (long i = 0; i < pmt->m_nCount; i++)
{
long nNewAvg;
hr = prghti[i].pl->GetAverageMethodTime(&nNewAvg);
long nDelta = nNewAvg - prghti[i].nAvgMethodTime;
prghti[i].nAvgMethodTime += (nDelta/4);
if (prghti[i].nAvgMethodTime < nLowestValue)
{
nLowestValue = prghti[i].nAvgMethodTime;
nLowestIndex = i;
}
}
pmt->m_phti = &prghti[nLowestIndex];
pmt->Unlock();
}
CoUninitialize();
}