|
|
//=--------------------------------------------------------------------------=
// Macros.Cpp
//=--------------------------------------------------------------------------=
// Copyright 1997 Microsoft Corporation. All Rights Reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//=--------------------------------------------------------------------------=
// Handy macros like the ones we use in the VB code base.
//=--------------------------------------------------------------------------=
#include "pch.h"
#ifdef DEBUG
#include <winuser.h>
// for ASSERT and FAIL
//
SZTHISFILE
//=--------------------------------------------------------------------------=
// Debug control switches
//=--------------------------------------------------------------------------=
DEFINE_SWITCH(fTraceCtlAllocs); // Trace all Heap allocations and frees
// fOutputFile should also be on with this switch
DEFINE_SWITCH(fOutputFile); // Logs all debug info in file:
// %CurrentDir%\ctldebug.log
DEFINE_SWITCH(fNoLeakAsserts); // No Heap memory leak asserts are displayed
// when turned on.
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !DEBUGGING HEAP MEMORY LEAKS!
// To debug a leak you need to figure out where and when the allocation was made.
// The top of the assert dialog will give you the OCX/DLL causing the leak.
// Goto Project/Build...Settings.
// On the Debug tab, select "additional DLLs"
// Locate and select the OCX/DLL causing the leak.
// Put a breakpoint on the noted line below.
// Goto Edit...Breakpoints.
// Select the new breakpoint.
// Press 'Condition'
// In the 'Enter number of times to skip before breaking' put the value of nAlloc-1.
// (if the leak was nAlloc=267 then you want to skip the breapoint 266 times, enter 266)
//
// WARNING: Each control (OCX/DLL) will have its own instance of the framewrk, and thus
// its own instance of the memory leak implementaion. Adding a breakpoint
// anywhere in the framewrk will actually add multiple breakpoints - one for
// each control.
// Go back to Edit...Breakpoints.
// Deselect or remove the breakpoints for the OCX's/DLL's not causing leaks
//
// Run your scenario.
// When you hit this breakpoint verify that pvAddress and nByteCount are correct and then
// look down the callstack to see where the allocation was made.
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
void PutBreakPointHere(void * pvAddress, ULONG nByteCount, ULONG nAlloc, char * szFile, ULONG uLine) { pvAddress=pvAddress; nAlloc=nAlloc; nByteCount=nByteCount; szFile=szFile; uLine=uLine; HINSTANCE hInstance = g_hInstance; // hInstance of the OCX/DLL calling this breakpoint
int PutBreakPointOnThisLine = 1; // <--- breakpoint here.
} // PutBreakPointHere
//=--------------------------------------------------------------------------=
//
// Debug Heap Memory Leak implementations
//
class CAddressNode { public: void * m_pv; // Address of memory block allocated
ULONG m_cb; // Size of allocation in BYTES
ULONG m_cAlloc; // Allocation pass count.
LPSZ m_szFile; // Source file where the allocation was made
ULONG m_uLine; // Source line number where the allocation was made
CAddressNode * m_pnNext; // Nodes are stored in a linked list
void * operator new(size_t cb); void operator delete(void * pv);
// We maintain a freelist to speed up allocation of AddressNodes.
static CAddressNode * m_pnFreeList; };
CAddressNode * m_rInstTable[NUM_INST_TABLE_ENTRIES]; // Hashing table of all instances of
// mem alloc
CAddressNode * m_pnEnumNode; // Next node for enumerator to return
UINT m_uEnumIndex; // Current index into m_rInstTable for enumerator
static ULONG m_cGlobalPassCount; // Pass count of allocation. Common to all heaps
ULONG m_cCurNumAllocs; // Current number of allocations
ULONG m_cNumAllocs; // Total number of allocations ever done.
ULONG m_cCurNumBytesAllocated; // Current number of bytes allocated.
ULONG m_cNumBytesAllocated; // Total bytes allocated.
ULONG m_HWAllocs; // High water allocations.
ULONG m_HWBytes; // High water bytes.
static ULONG m_OverallCurAlloc; // These are overall statistics to since we
static ULONG m_OverallCurBytes; // wouldn't mind the overall high water.
static ULONG m_OverallHWAlloc; static ULONG m_OverallHWBytes;
// Forward declarations
VOID AddInst(VOID * pv, DWORD dwBytes, LPSZ szFile, UINT uLine); VOID DebugInst(ULONG cb); VOID AnalyzeInst(LPVOID pv); VOID DumpInst(CAddressNode * pn, LPTSTR lpTypeofAlloc); LPSTR DumpInstTable(LPSTR lpLeak); VOID DeleteInst(LPVOID pv); VOID VerifyHeaderTrailer(CAddressNode * pn); VOID CheckForLeaks(VOID); VOID HeapCheck(VOID); VOID OutputToFile(LPSTR szOutput); CAddressNode * FindInst(LPVOID pv); CAddressNode * EnumReset(); CAddressNode * EnumNext();
// Initialize a header and trailer for all memory to be allocated.
// Use 8 bytes so it is also compatible with RISC machines.
char * g_szHeader = "HEADHEAD"; char * g_szTrailer = "END!END!";
#define HEADERSIZE 8 // # of bytes of block header
// 0 ==> no block header signature
#define TRAILERSIZE 8 // # of bytes of block trailer
// 0 ==> no block trailer signature
//=--------------------------------------------------------------------------=
// CtlHeapAllocImpl:
// Debug wrapper for HeapAlloc to track memory leaks:
//=--------------------------------------------------------------------------=
LPVOID CtlHeapAllocImpl( HANDLE g_hHeap, DWORD dwFlags, DWORD dwBytesRequested, LPSTR lpszFile, UINT line ) { LPVOID lpvRet; DWORD dwBytes; LPTSTR lpTypeofAlloc = "HeapAlloc ";
// If someone tries to allocate memory before PROCCESS_ATTATCH (such as in a
// global constructor), do not track it because neither our heap nor our
// hInstance have been initialized yet.
//
if (!g_fInitCrit) { g_flagConstructorAlloc = TRUE; return HeapAlloc(g_hHeap, dwFlags, dwBytesRequested); }
// Increase size to make space for header and trailer signatures
dwBytes = dwBytesRequested + HEADERSIZE + TRAILERSIZE;
// Allocate memory
lpvRet = HeapAlloc(g_hHeap, dwFlags, dwBytes); if (lpvRet) { // Initialize memory (non-zero)
if (!(dwFlags & HEAP_ZERO_MEMORY)) memset(lpvRet, 0xAF, dwBytes);
// Add instance to hash table
AddInst(lpvRet, dwBytesRequested, lpszFile, line);
// Trace allocations if switch is on
if (FSWITCH(fTraceCtlAllocs)) { CAddressNode *pn = FindInst(lpvRet); DumpInst(pn, lpTypeofAlloc); }
// Advance pointer past header signature.
lpvRet = (LPVOID) ((char *)lpvRet + HEADERSIZE); } return lpvRet; } // CtlHeapAllocImpl
//=--------------------------------------------------------------------------=
// CtlHeapReAllocImpl:
//
//=--------------------------------------------------------------------------=
LPVOID CtlHeapReAllocImpl( HANDLE g_hHeap, DWORD dwFlags, LPVOID lpvMem, DWORD dwBytesRequested, LPSTR lpszFile, UINT line ) { LPVOID lpvRet; CAddressNode * pn; int byte; DWORD cbOffset, dwBytes; LPTSTR lpTypeofAlloc = "HeapReAlloc ";
// Move pointer to beginning of header
lpvMem = (LPVOID)((char *)lpvMem - HEADERSIZE);
// Find instance in hash table
pn = FindInst(lpvMem); if (!pn) { FAIL("CtlHeapReAllocImpl - could not find lpvMem in the instance table. See debug \
output for more info."); AnalyzeInst(lpvMem); return 0; }
// Increase size to make space for header and trailer signatures
dwBytes = dwBytesRequested + HEADERSIZE + TRAILERSIZE; lpvRet = HeapReAlloc(g_hHeap, dwFlags, lpvMem, dwBytes); if (lpvRet) { // If the reallocation grew, we must intialize new memory
if (dwBytesRequested > pn->m_cb) { if (dwFlags & HEAP_ZERO_MEMORY) byte = 0x0; else byte = 0xAF;
// Get the byte offset of trailer in the old allocation
cbOffset = pn->m_cb + HEADERSIZE; memset((char *)lpvRet + cbOffset, byte, dwBytes - cbOffset); } // Update hash table
EnterCriticalSection(&g_csHeap); DeleteInst(lpvMem); AddInst(lpvRet, dwBytesRequested, lpszFile, line); LeaveCriticalSection(&g_csHeap);
// Trace Allocations if switch is on
if (FSWITCH(fTraceCtlAllocs)) { CAddressNode *pn = FindInst(lpvRet); DumpInst(pn, lpTypeofAlloc); }
// Advance pointer past header signature.
lpvRet = (LPVOID)((char *)lpvRet + HEADERSIZE); } return lpvRet; } // CtlHeapReAllocImpl
//=--------------------------------------------------------------------------=
// CtlHeapFreeImpl:
// Debug wrapper for HeapFree
//=--------------------------------------------------------------------------=
BOOL CtlHeapFreeImpl( HANDLE g_hHeap, DWORD dwFlags, LPVOID lpvMem ) { BOOL fRet = FALSE; CAddressNode * pn; LPTSTR lpTypeofAlloc = "HeapFree ";
// If someone tries to de-allocate memory after PROCCESS_DETATCH (such as in a
// global destructor), Re-initialize critical section and free memory.
//
if (!g_fInitCrit) InitializeCriticalSection(&g_csHeap);
// Move pointer to beginning of header
lpvMem = (LPVOID) ((char *)lpvMem - HEADERSIZE);
// Find the instance in the hash table
pn = FindInst(lpvMem); if (pn) { // Verify the memory has not been overwritten
VerifyHeaderTrailer(pn);
// Trace allocations if switch is on
if (FSWITCH(fTraceCtlAllocs)) { CAddressNode *pn = FindInst(lpvMem); DumpInst(pn, lpTypeofAlloc); }
// Free memory -- NOTE: WinNT will set free memory to 0xEEFEEEFE which is "����"
fRet = HeapFree(g_hHeap, 0, lpvMem); if (!fRet) FAIL("CtlHeapFreeImpl - lpvMem was found to be allocated in the heap passed in \
but HeapFree() failed. Maybe the pointer was already freed."); }
// Remove instance from hash table
if (fRet) DeleteInst(lpvMem);
// Make sure this memory wasn't allocated in a global constructor
else if (!g_flagConstructorAlloc) { FAIL("CtlHeapFreeImpl - could not find lpvMem in the instance table. See debug \
output for more info."); AnalyzeInst(lpvMem); } else fRet = TRUE; // If called after PROCESS_DETATCH delete critical section and Check for leaks again
// NOTE: Only the LAST Assert will have the exact leak information. All previous
// Asserts will not take into account a HeapFree which occurs after PROCESS_DETACH.
// This only occurs in controls using global static destructors.
if (!g_fInitCrit) { CheckForLeaks(); DeleteCriticalSection(&g_csHeap); }
return fRet; } // CtlHeapFreeImpl
//=--------------------------------------------------------------------------=
// CheckForLeaks:
// We are calling PROCESS_DETATCH so check if hash table is empty. If not
// dump info on memory that has been leaked.
//=--------------------------------------------------------------------------=
VOID CheckForLeaks(VOID) { CAddressNode * pn = EnumReset(); BOOL IsEmpty = (pn == NULL); // FALSE if there are leaks
// First check for memory trashing of any leaked memory
HeapCheck(); if (!IsEmpty) {
// First find out which OCX/DLL is leaking
TCHAR lpCtlName[128]; DWORD nSize = 128; DWORD fValidPath; fValidPath = GetModuleFileName(g_hInstance, (LPTSTR)lpCtlName, nSize);
LPSTR lpLeaks; // Allocate some memory to hold the data but use GlobalAlloc since we
// don't want to use the vb memory stuff since it will muck things up.
lpLeaks = (LPSTR)GlobalLock(GlobalAlloc(GMEM_MOVEABLE,128));
lstrcpy(lpLeaks, lpCtlName); lstrcat(lpLeaks, " has leaked memory.\nUse PutBreakPointHere() in macros.cpp to debug.\r\n");
// Collect all leak info
lpLeaks = DumpInstTable(lpLeaks); // Dump output to file if "fOutputFile" switch is on
if (FSWITCH(fOutputFile)) OutputToFile(lpLeaks);
// Dump output to an assert as long as "fNoLeakAsserts" is off
else if (!FSWITCH(fNoLeakAsserts)) { // Truncate output so it fits into DisplayAssert (512 Max)
if (lstrlen(lpLeaks) > 500) { lstrcpyn(lpLeaks, lpLeaks, 500); lstrcat(lpLeaks, "\nMore..."); } DisplayAssert(lpLeaks, "FAIL", NULL, 0); }
// Release memory used to store leak info
GlobalUnlock((HGLOBAL)GlobalHandle(lpLeaks)), (BOOL)GlobalFree((HGLOBAL)GlobalHandle(lpLeaks));
} return; } // CheckForLeaks
//=--------------------------------------------------------------------------=
// AddInst:
// A heap allocation occured so here we add the allocation information to
// the instance table. To debug memory leaks where you need to use pass
// counts, set a passcount breakpoint in this function using the passcount
// value given in the debug output.
//=--------------------------------------------------------------------------=
VOID AddInst( VOID * pv, DWORD dwBytes, LPSZ szFile, UINT uLine ) { UINT uHash; CAddressNode * pn = new CAddressNode(); ASSERT(pn,""); EnterCriticalSection(&g_csHeap);
m_cGlobalPassCount++;
pn->m_pv = pv; // Memory address of allocation
pn->m_cb = dwBytes; // Bytes requested to be allocated
pn->m_cAlloc = m_cGlobalPassCount; // This is the pass count value in debug output.
pn->m_szFile = szFile; // Source file the allocation call was made
pn->m_uLine = uLine; // Line number in source file.
PutBreakPointHere(pv, dwBytes, m_cGlobalPassCount, szFile, uLine);
// Add instance to proper position in table
uHash = HashInst(pv); pn->m_pnNext = m_rInstTable[uHash]; m_rInstTable[uHash] = pn;
// Copy header and trailer signatures.
memcpy((char *)pv, g_szHeader, HEADERSIZE); memcpy((char *)pv + HEADERSIZE + dwBytes, g_szTrailer, TRAILERSIZE);
LeaveCriticalSection(&g_csHeap);
// Track extra memory debug info
DebugInst( dwBytes ); } // AddInst
//=--------------------------------------------------------------------------=
// DebugInst:
// Updates the memory debug information
//=--------------------------------------------------------------------------=
VOID DebugInst( ULONG cb ) { EnterCriticalSection(&g_csHeap);
++m_cCurNumAllocs; ++m_cNumAllocs; ++m_OverallCurAlloc; m_cCurNumBytesAllocated+=cb; m_cNumBytesAllocated+=cb; m_OverallCurBytes+=cb;
m_HWAllocs = (m_HWAllocs < m_cCurNumAllocs) ? m_cCurNumAllocs : m_HWAllocs; m_HWBytes = (m_HWBytes < m_cCurNumBytesAllocated) ? m_cCurNumBytesAllocated : m_HWBytes; m_OverallHWAlloc = (m_OverallHWAlloc < m_OverallCurAlloc) ? m_OverallCurAlloc : m_OverallHWAlloc; m_OverallHWBytes = (m_OverallHWBytes < m_OverallCurBytes) ? m_OverallCurBytes : m_OverallHWBytes;
LeaveCriticalSection(&g_csHeap);
} // DebugInst
//=--------------------------------------------------------------------------=
// FindInst:
// Give a pointer to an allocation, return a pointer to the debug
// allocation information.
//=--------------------------------------------------------------------------=
CAddressNode * FindInst( LPVOID pv ) { CAddressNode * pn;
EnterCriticalSection(&g_csHeap);
pn = m_rInstTable[HashInst(pv)]; while (pn && pn->m_pv != pv) pn = pn->m_pnNext;
LeaveCriticalSection(&g_csHeap); return pn;
} // FindInst
//=--------------------------------------------------------------------------=
// AnalyzeInst:
// Given a pointer try determine if it is a valid Read and Write pointer
// and if it was allocated.
//=--------------------------------------------------------------------------=
VOID AnalyzeInst( LPVOID pv ) { LPTSTR lpTypeofAlloc = "Bad lpvMem "; CAddressNode * pn = NULL;
// Either we have a bad pointer or the pointer does not point to any
// known heap allocations. Here we check if it points to readable or
// writable memory.
BOOL fBadPointer = (IsBadReadPtr(pv, 4) || IsBadWritePtr(pv, 4)); // Report what we know about the memory address
if (fBadPointer) DebugPrintf("AnalyzeInst found that pointer pv=0x%lX is not writable\n\r" \ "or readable. The allocation is either outside the addressable range\n\r" \ "for this operating system or the allocation was already freed.\n\r",pv); else DebugPrintf("AnalyzeInst found that pointer pv=0x%lX is readable and writable,\n\r" \ "so the allocation was made without being added to instance table\n\r" \ "(prior to PROCESS_ATTATCH), or the memory was already freed.\n\r",pv); } // AnanlyzeInst
//=--------------------------------------------------------------------------=
// DumpInst:
// Dump instance information out to an assert window.
//=--------------------------------------------------------------------------=
VOID DumpInst( CAddressNode * pn, LPTSTR lpTypeofAlloc ) { char szOutput[255];
// Format output
wsprintf(szOutput, "%s: %s(%u) Address=0x%lx nAlloc=%ld Bytes=%ld\r\n", lpTypeofAlloc, pn->m_szFile, pn->m_uLine, (ULONG)pn->m_pv, (ULONG)pn->m_cAlloc, (ULONG)pn->m_cb); // Dump output to file if switch is turned on
if (FSWITCH(fOutputFile)) OutputToFile(szOutput); else if (FSWITCH(fNoLeakAsserts)) DebugPrintf(szOutput); // Else display output in assert
else DisplayAssert(szOutput, "FAIL", _szThisFile, __LINE__);;
} // DumpInst
//=--------------------------------------------------------------------------=
// DumpInstTable:
// Memory leak has been detected so dump the entire instance table.
//=--------------------------------------------------------------------------=
LPSTR DumpInstTable( LPSTR lpLeak ) { CAddressNode * pn = EnumReset(); DWORD sizeoflpLeak; LPSTR lpTemp;
EnterCriticalSection(&g_csHeap);
DebugPrintf(lpLeak);
while (pn) { // Format the leak info
char szOut[250] = {NULL}; wsprintf(szOut, "\t%s(%u) Address=0x%lx nAlloc=%ld Bytes=%ld\r\n", pn->m_szFile, pn->m_uLine, (ULONG)pn->m_pv, (ULONG)pn->m_cAlloc, (ULONG)pn->m_cb);
DebugPrintf(szOut); // Convert lpLeak to a handle and get its current allocation size
sizeoflpLeak = GlobalSize(GlobalHandle(lpLeak));
// Reallocate memory to make space for more leak info
lpTemp = (LPSTR) (GlobalUnlock((HGLOBAL)GlobalHandle(lpLeak)), GlobalLock(GlobalReAlloc((HGLOBAL)GlobalHandle(lpLeak), sizeoflpLeak + lstrlen(szOut) + 1, GMEM_MOVEABLE)));
// Add new leak info to lpLeak
if(lpTemp) { lpLeak = lpTemp; lstrcat(lpLeak, szOut); }
// Get the next leak
pn = EnumNext(); } LeaveCriticalSection(&g_csHeap); return lpLeak;
} // DumpInstTable
//=--------------------------------------------------------------------------=
// DeleteInst:
// A heap allocation got free or was reallocated so remove the
// information from the instance table and check for memory trashing.
//=--------------------------------------------------------------------------=
VOID DeleteInst( LPVOID pv ) { CAddressNode ** ppn, * pnDead; ppn = &m_rInstTable[HashInst(pv)];
EnterCriticalSection(&g_csHeap); // Find allocation instance
while (*ppn != NULL) { if ((*ppn)->m_pv == pv) { pnDead = *ppn; *ppn = (*ppn)->m_pnNext;
// Correct memory debug info
--m_cCurNumAllocs; m_cCurNumBytesAllocated -= pnDead->m_cb; --m_OverallCurAlloc; m_OverallCurBytes -= pnDead->m_cb;
// Remove instance
delete pnDead; LeaveCriticalSection(&g_csHeap); return; } // if
ppn = &((*ppn)->m_pnNext); } // while
FAIL("DeleteInst - memory instance not found"); } // DeleteInst
//=--------------------------------------------------------------------------=
// VerifyHeaderTrailer:
// Inspect allocation for header and trailer signature overwrites
//=--------------------------------------------------------------------------=
VOID VerifyHeaderTrailer( CAddressNode * pn ) { LPTSTR lpTypeofAlloc = "Memory trashed ";
//Verify the header
if (memcmp((char *)pn->m_pv, g_szHeader, HEADERSIZE) != 0) { FAIL("Heap block header has been trashed."); DebugPrintf("Heap block header trashed."); DebugPrintf("\r\n"); DumpInst(pn, lpTypeofAlloc); }
//Verify the trailer
if (memcmp((char *)pn->m_pv + pn->m_cb + HEADERSIZE, g_szTrailer, TRAILERSIZE) != 0) { FAIL("Heap block trailer has been trashed."); DebugPrintf("Heap block trailer trashed."); DebugPrintf("\r\n"); DumpInst(pn, lpTypeofAlloc); } return;
} // VerifyHeaderTrailer
//=--------------------------------------------------------------------------=
// HeapCheck:
// Inspect all of the allocations for header and trailer signature
// overwrites.
//=--------------------------------------------------------------------------=
VOID HeapCheck(VOID) { ASSERT(HeapValidate(g_hHeap, 0, NULL) != 0, "OS Says heap is corrupt");
CAddressNode * pn = EnumReset(); while (pn) { VerifyHeaderTrailer(pn); pn = EnumNext(); } return; } // HeapCheck
//=-------------------------------------------------------------------------=
// For use with CAddresssNode
//=-------------------------------------------------------------------------=
#define MEM_cAddressNodes 128 // Nodes are block allocated
#define UNUSED(var) ((var) = (var)) // Used to avoid warnings
// The free list is common
CAddressNode * CAddressNode::m_pnFreeList = NULL;
//=--------------------------------------------------------------------------=
// CAddressNode::operator new:
// Returns a pointer to an allocated address node. If there are none on
// the free list then we allocate a block of address nodes, chain them
// together and add them to the free list. These nodes are never
// actually freed so it is ok to allocate them in blocks.
//=--------------------------------------------------------------------------=
void * CAddressNode::operator new( size_t cb ) { CAddressNode * pn; UNUSED(cb);
EnterCriticalSection(&g_csHeap); // needed for static m_pnFreeList
if (m_pnFreeList == NULL) { UINT cbSize = sizeof(CAddressNode) * MEM_cAddressNodes; //allocate a block
pn = (CAddressNode *) HeapAlloc(g_hHeap, 0, cbSize); //chain all except the first node together. the first node
//is the one returned
for (int i = 1; i < MEM_cAddressNodes - 1; ++i) pn[i].m_pnNext = &pn[i+1]; pn[MEM_cAddressNodes - 1].m_pnNext = NULL; m_pnFreeList = &pn[1]; } else { pn = m_pnFreeList; m_pnFreeList = pn->m_pnNext; }
LeaveCriticalSection(&g_csHeap); return pn; } // CAddressNode::operator new
//=--------------------------------------------------------------------------=
// CAddressNode::operator delete
// Return the address node to the free list. We never actually free
// the node since nodes are allocated in blocks.
//=--------------------------------------------------------------------------=
void CAddressNode::operator delete( void * pv ) { EnterCriticalSection(&g_csHeap); // needed for static m_pnFreeList
CAddressNode * pn = (CAddressNode *) pv; pn->m_pnNext = m_pnFreeList; m_pnFreeList = pn;
LeaveCriticalSection(&g_csHeap); } // CAddressNode::operator delete
//=--------------------------------------------------------------------------=
// EnumReset:
// Reset the enumerator and return the first node. NULL if empty.
//=--------------------------------------------------------------------------=
CAddressNode * EnumReset() { m_pnEnumNode = NULL; for (m_uEnumIndex = 0; m_uEnumIndex < NUM_INST_TABLE_ENTRIES; ++m_uEnumIndex) { m_pnEnumNode = m_rInstTable[m_uEnumIndex]; if (m_pnEnumNode != NULL) return m_pnEnumNode; } return NULL; //Instance table is empty
} // EnumReset
//=--------------------------------------------------------------------------=
// EnumNext:
// Return the next node in the enumeration. m_pnEnumNode points to the last
// node returned. It is NULL if no more left.
//=--------------------------------------------------------------------------=
CAddressNode * EnumNext() { ASSERT(m_uEnumIndex <= NUM_INST_TABLE_ENTRIES, "");
if (m_pnEnumNode == NULL) return NULL; //end of enumeration
m_pnEnumNode = m_pnEnumNode->m_pnNext; if (m_pnEnumNode == NULL) { //at end of this linked list so search for next list
m_uEnumIndex++; while (m_uEnumIndex < NUM_INST_TABLE_ENTRIES && m_rInstTable[m_uEnumIndex] == NULL) m_uEnumIndex++; if (m_uEnumIndex < NUM_INST_TABLE_ENTRIES) m_pnEnumNode = m_rInstTable[m_uEnumIndex]; } return m_pnEnumNode; } // EnumNext
//=---------------------------------------------------------------------------=
// OutputToFile:
// Dumps output to file "ctldebug.log"
//=---------------------------------------------------------------------------=
VOID OutputToFile ( LPSTR szOutput ) { DWORD nPathSize; DWORD nDirPathSize = 128; TCHAR lpFilePath[128]; LPCTSTR lpFileName = "\\CtlDebug.log"; HANDLE hFile; BOOL fWritten, fClosed = FALSE; DWORD nBytesWritten;
// Create path to output file
nPathSize = GetCurrentDirectory(nDirPathSize, (LPTSTR)lpFilePath); if (nPathSize == 0) FAIL("Unable to get current directory..."); lstrcat(lpFilePath, lpFileName);
// Open and write to file
hFile = CreateFile((LPCTSTR)lpFilePath, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); DWORD SetPtr = SetFilePointer(hFile, NULL, NULL, FILE_END); fWritten = WriteFile(hFile, (LPCVOID)szOutput, (DWORD)strlen(szOutput), &nBytesWritten, NULL); if (!fWritten) FAIL("Unable to write output to file...");
// Close file handle
fClosed = CloseHandle(hFile); if (!fClosed) FAIL("Unable to close output file...");
} // OutputToFile
//
// End of Debug Memory Leak implemntation
//
//=--------------------------------------------------------------------------=
//=--------------------------------------------------------------------------=
// This routine outputs through DebugPrintf some information if the
// given hr fails to succeed. This is used by RRETURN to output where
// a function that returns a failing error code.
//=--------------------------------------------------------------------------=
HRESULT HrDebugTraceReturn ( HRESULT hr, char *pszFile, int iLine ) { // We only output information if the hr fails.
if (FAILED(hr)) { char szMessageError[128]; szMessageError[0] = '\0'; BOOL fMessage;
#if RBY_MAC
fMessage = FALSE; // FormatMessage not available on the mac
#else
// Get the message from the system
// CONSIDER, t-tshort 10/95: Getting some messages from us instead
// of the system?
fMessage = FormatMessage(FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US), szMessageError, sizeof(szMessageError), NULL); #endif
// Erps didn't get a message.
if(!fMessage) lstrcpy(szMessageError,"Unknown Hresult");
// Output the information that we want.
DebugPrintf("FAILED RETURN: %s(%d) : 0x%08lx, %s\n", pszFile, iLine, hr, szMessageError); }
return hr; }
//---------------------------------------------------------------------
// The following is a common output formatting buffer shared by several
// of the following debug routines.
//---------------------------------------------------------------------
char s_rgchOutput[2048]; // pretty big...
//=--------------------------------------------------------------------------=
// Emit debugging information
//=--------------------------------------------------------------------------=
void _DebugOutput(char* pszOutput) { OutputDebugString(pszOutput); }
//=--------------------------------------------------------------------------=
// Emit a formatted debugging string to the location specified in
// the debug options dialog.
//=--------------------------------------------------------------------------=
void _DebugPrintf(char* pszFmt, ...) { va_list args;
va_start(args, pszFmt); wvsprintf(s_rgchOutput, pszFmt, args); va_end(args);
// sqwak if we overrun the formatting buffer!
ASSERT(strlen(s_rgchOutput) < sizeof(s_rgchOutput), "");
_DebugOutput(s_rgchOutput); }
//=--------------------------------------------------------------------------=
// Conditional form of DebugPrintf
//=--------------------------------------------------------------------------=
void _DebugPrintIf(BOOL fPrint, char* pszFmt, ...) { va_list args;
if (!fPrint) return;
va_start(args, pszFmt); wvsprintf(s_rgchOutput, pszFmt, args); va_end(args);
// sqwak if we overrun the formatting buffer!
ASSERT(strlen(s_rgchOutput) < sizeof(s_rgchOutput), "");
_DebugOutput(s_rgchOutput); }
#endif // DEBUG
|