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