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