Figure 1  
IMalloc and IMallocSpy

interface IMalloc : public IUnknown
{
    void *Alloc(ULONG cb) = 0;
    void *Realloc(void *pv, ULONG cb) = 0;
    void Free(void *pv) = 0;
    ULONG GetSize(void *pv) = 0;
    int DidAlloc(void *pv) = 0;
    void HeapMinimize( void) = 0;
};

interface IMallocSpy : public IUnknown
{
    ULONG PreAlloc(ULONG cbRequest) = 0;
    void *PostAlloc(void __RPC_FAR *pActual) = 0;
    void *PreFree(void __RPC_FAR *pRequest, BOOL fSpyed) = 0;
        
    void PostFree(BOOL fSpyed) = 0;
        
    ULONG PreRealloc(void *pRequest,
                     ULONG cbRequest,
                     void **ppNewRequest,
                     BOOL fSpyed) = 0;
    void *PostRealloc(void *pActual, BOOL fSpyed) = 0;
    void *PreGetSize(void *pRequest, BOOL fSpyed) = 0;
    ULONG PostGetSize(ULONG cbActual, BOOL fSpyed) = 0;
    void *PreDidAlloc(void *pRequest, BOOL fSpyed) = 0;
    int PostDidAlloc(void *pRequest, BOOL fSpyed, int fActual) = 0;
    void PreHeapMinimize(void) = 0;
    void PostHeapMinimize( void) = 0;
};


Figure 2  
IMallocSpyObjEvents

[   uuid(A3693113-D544-11D2-8042-BAA003000000),
    object,
    helpstring("_IMallocSpyObjEvents Interface")
]
interface IMallocSpyObjEvents : IDispatch
{   // IMallocSpy that Visual Basic can use...
    // This shouldn't require interaction from Visual Basic.
    [id(10)]HRESULT PreAlloc(
        // "Number of bytes specified by caller"
        [in] long cbRequestByCaller,
        // "Number of bytes actually allocated"
        [in] long cbToBeAllocated
    );

    [id(11)] HRESULT PostAlloc(
        // "Pointer returned by task allocator"
        [in] long pReturnedByTaskAllocator,
        // "Pointer to betinning of block
        [in] long pBeginningOfBlock
    );

    [id(12)]HRESULT PreFree(
        // "Address of pointer to be freed
        [in] long pToBeFreed
    );

    [id(13)]HRESULT  PostFree(
        // the pointer was freed
    );

    [id(14)]HRESULT PreRealloc(
        // pointer to bytes to re-alloc. Requested by caller
        [in] long pRequestByCaller,
        // number of bytes to re-alloc. Requested by caller
        [in] long   cbRequestByCaller
    );

    [id(15)]HRESULT PostRealloc(
        // Pointer to bytes returned by task allocator
        [in] long pReturnedByTaskAllocator,
        // Pointer to bytes returned to caller
        [in] long pBeginningOfBlock
    );

    [id(16)]HRESULT PreGetSize(
        // pointer to bytes to find out size of. Requested by caller
        [in] long pRequestedByCaller,
        // pointer to actual bytes to find out size of
        [in] long pActual
    );

    [id(17)] HRESULT PostGetSize(
        // Actual number of bytes 
        [in] long cbActual,
        // number of bytes returned by task allocator
        [in] long cbReturnedByTaskAllocator
    );

    [id(18)]HRESULT PreDidAlloc(
        // pointer passed by caller
        [in] long pRequestedByCaller,
        // pointer to actually check
        [in] long pActual 
    );

    [id(19)] HRESULT PostDidAlloc(
        //  pointer requested by caller
        [in] long pRequestedByCaller,
        // actual pointer checked
        [in] long   fActual,
        // flag returned to caller
        [in] long   fReturnedToCaller
    );

    [id(21)]HRESULT  PreHeapMinimize(void);
    [id(22)]HRESULT  PostHeapMinimize(void);
};

Figure 3  
Key Elements of CMallocSpyObj

class CMallocSpyObj : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CMallocSpyObj, &CLSID_MallocSpyObj>,
    public ISupportErrorInfo,
    public IConnectionPointContainerImpl<CMallocSpyObj>,
    public IDispatchImpl<IMallocSpyObj, &IID_IMallocSpyObj, 
                         &LIBID_MALLOCSPYSVRLib>,
    public IMallocSpy
{

protected:
    IMallocSpyObjEvents* m_pMallocSpyObjEvents;
    bool m_bIsSpying;

BEGIN_COM_MAP(CMallocSpyObj)
    COM_INTERFACE_ENTRY(IMallocSpy)
    COM_INTERFACE_ENTRY(IMallocSpyObj)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(ISupportErrorInfo)
END_COM_MAP()
};


Figure 4  
Implementing IMallocSpy

STDMETHOD_(ULONG, PreAlloc)(ULONG cbRequest){    
    // Called before allocating any memory from the
    // task allocator...
    if(m_pMallocSpyObjEvents){
        m_pMallocSpyObjEvents->PreAlloc(cbRequest, cbRequest);
    }
    return cbRequest;
}
STDMETHOD_(void*, PostAlloc)(void *pActual){
    // Called after allocating any memory from the
      // task allocator...
    // Show the pointer value on the screen. Then add it to the 
    // list of pointers held by the malloc spy...
    if(m_pMallocSpyObjEvents){
        m_pMallocSpyObjEvents->PostAlloc((long)pActual, (long)pActual);
    }
    return pActual;
}
STDMETHOD_(void*, PreFree)(void *pRequest, BOOL fSpyed){
    // Called before freeing any memory from the
    // task allocator...
    if(m_pMallocSpyObjEvents){
        m_pMallocSpyObjEvents->PreFree((long)pRequest);
    }
    return pRequest;
}
STDMETHOD_(void, PostFree)(BOOL fSpyed){
    // Called after freeing any memory from the
    //  task allocator...
    if(m_pMallocSpyObjEvents){
        m_pMallocSpyObjEvents->PostFree();
    }
    return;
}
STDMETHOD_(ULONG, PreRealloc)(void *pRequest, ULONG cbRequest,
                              void** ppNewRequest, BOOL fSpyed){
    // Called before reallocating any memory from the
    // task allocator...
    *ppNewRequest = pRequest;
    return cbRequest;
}
STDMETHOD_(void*, PostRealloc)(void *pActual, BOOL fSpyed){
    return pActual;
}
STDMETHOD_(void*, PreGetSize)(void *pRequest, BOOL fSpyed){
    return pRequest;
}
STDMETHOD_(ULONG, PostGetSize)(ULONG cbActual, BOOL fSpyed){
    return cbActual;
}
STDMETHOD_(void*, PreDidAlloc)(void *pRequest, BOOL fSpyed){
    // Called before reporting whether a pointer was
    // actually allocated from the task allocator...
    return pRequest;
}
STDMETHOD_(int, PostDidAlloc)( void *pRequest, BOOL fSpyed, int fActual){
    // Called after reporting whether a pointer was
    // actually allocated from the task allocator...
    return fActual;
}
STDMETHOD_(void, PreHeapMinimize)(void){
    // This is called before the task allocator heap is minimized 
    return;
}
STDMETHOD_(void, PostHeapMinimize)(void){
    // This is called after the task allocator heap is minimized 
    return;
}

Figure 5  
StartSpying and StopSpying

STDMETHODIMP CMallocSpyObj::StartSpying(){
    HRESULT hr = S_OK;
    if(!m_bIsSpying){
        IMallocSpy* pMallocSpy = NULL;
        hr = QueryInterface(IID_IMallocSpy, (void**)&pMallocSpy);
        // CoRegisterMallocSpy add refs
        ULONG nRef = pMallocSpy->Release();
        if(SUCCEEDED(hr)){
            hr = CoRegisterMallocSpy(pMallocSpy);
            if(SUCCEEDED(hr)){
                m_bIsSpying = TRUE;
            }else{
                OutputDebugString("Task allocator spy \
                                  already registered\n");
            }
        }
    }
    return hr;
}
STDMETHODIMP CMallocSpyObj::StopSpying(){
    // TODO: Add your implementation code here
    HRESULT hr = S_OK;
    if(m_bIsSpying)    {
        if(m_pMallocSpyObjEvents){
            ULONG nRefs = m_pMallocSpyObjEvents->Release();
            m_pMallocSpyObjEvents = NULL;
        }
        hr = CoRevokeMallocSpy();
        if(!SUCCEEDED(hr)){
            if(hr == E_ACCESSDENIED){
                OuputDebugString("Task allocator spy couldn't be revoked, \
                                 which means there are some outstanding \
                                 allocations-- probably in the BSTR cache");
                return hr;
            }else{
                OuputDebugString ("Problem revoking IMallocSpy");
                return hr;
            }
        }else{
            m_bIsSpying = FALSE;
            OutputDebugString("Revoked IMallocSpy\n");
            return S_OK;
        }
    }
    return S_OK;
}
STDMETHODIMP CMallocSpyObj::SetMallocSpyEventSink(IUnknown *pUnk){
    // TODO: Add your implementation code here
    if(m_pMallocSpyObjEvents){
        m_pMallocSpyObjEvents->Release();
        m_pMallocSpyObjEvents = NULL;
    }
    if(pUnk){
        pUnk->QueryInterface(IID_IMallocSpyObjEvents, 
            (void**)&m_pMallocSpyObjEvents);
    }
    return S_OK;
}


Figure 6  
The Visual Basic Add-in

Public VBInstance As VBIDE.VBE
Public Connect As Connect
Public memspy As MallocSpyObj
Dim IsSpying As Boolean
Implements IMallocSpyObjEvents
Option Explicit

Private Sub ClearInfo_Click()
    MallocSpyInfoLB.Clear
End Sub
Private Sub Form_Load()
    IsSpying = False
End Sub
Private Sub Form_Unload(Cancel As Integer)
    StopSpying_Click
End Sub
Private Sub StartSpying_Click()
    If IsSpying = False Then
        Set memspy = New MallocSpyObj
        IsSpying = True    
        memspy.SetMallocSpyEventSink Me
    End If    
    On Error GoTo catchblock
    memspy.StartSpying
    Exit Sub
catchblock:
    MallocSpyInfoLB.AddItem Err.Description
End Sub
Private Sub StopSpying_Click()
    On Error GoTo catchblock    
    If IsSpying = True Then
        memspy.StopSpying
        IsSpying = False
    End If    
    Exit Sub
catchblock:
    MallocSpyInfoLB.AddItem Err.Description
End Sub
Private Sub IMallocSpyObjEvents_PostAlloc(ByVal pReturnedByTaskAllocator As Long,
    ByVal pBeginningOfBlock As Long)
    MallocSpyInfoLB.AddItem "Post Allocing"
End Sub
Private Sub IMallocSpyObjEvents_PostDidAlloc(ByVal pRequestedByCaller As Long,
    ByVal fActual As Long, ByVal fReturnedToCaller As Long)
    MallocSpyInfoLB.AddItem "Post did alloc"
End Sub
Private Sub IMallocSpyObjEvents_PostFree()
    MallocSpyInfoLB.AddItem "Post free"
End Sub
Private Sub IMallocSpyObjEvents_PostGetSize(ByVal cbActual As Long,
    ByVal cbReturnedByTaskAllocator As Long)
    MallocSpyInfoLB.AddItem "Post GetSize"
End Sub
Private Sub IMallocSpyObjEvents_PostHeapMinimize()
    MallocSpyInfoLB.AddItem "Post HeapMinimize"
End Sub
Private Sub IMallocSpyObjEvents_PostRealloc(
    ByVal pReturnedByTaskAllocator As Long, ByVal pBeginningOfBlock As Long)
    MallocSpyInfoLB.AddItem "Post Realloc"
End Sub
Private Sub IMallocSpyObjEvents_PreAlloc(ByVal cbRequestByCaller As Long,
    ByVal cbToBeAllocated As Long)
    MallocSpyInfoLB.AddItem "Pre-Allocing"
End Sub
Private Sub IMallocSpyObjEvents_PreDidAlloc(ByVal pRequestedByCaller As Long,
    ByVal pActual As Long)
    MallocSpyInfoLB.AddItem "Pre DidAlloc"
End Sub
Private Sub IMallocSpyObjEvents_PreFree(ByVal pToBeFreed As Long)
    MallocSpyInfoLB.AddItem "PreFree"
End Sub
Private Sub IMallocSpyObjEvents_PreGetSize(ByVal pRequestedByCaller As Long,
    ByVal pActual As Long)
    MallocSpyInfoLB.AddItem "Pre GetSize"
End Sub
Private Sub IMallocSpyObjEvents_PreHeapMinimize()
    MallocSpyInfoLB.AddItem "Pre HeapMinimize"
End Sub
Private Sub IMallocSpyObjEvents_PreRealloc(ByVal pRequestByCaller As Long,
    ByVal cbRequestByCaller As Long)
    MallocSpyInfoLB.AddItem "Pre Realloc"
End Sub