]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/cpp/DataBoardTest/DataBoardTest/Stackwalker.cpp
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / cpp / DataBoardTest / DataBoardTest / Stackwalker.cpp
diff --git a/bundles/org.simantics.databoard/cpp/DataBoardTest/DataBoardTest/Stackwalker.cpp b/bundles/org.simantics.databoard/cpp/DataBoardTest/DataBoardTest/Stackwalker.cpp
new file mode 100644 (file)
index 0000000..10afabe
--- /dev/null
@@ -0,0 +1,2570 @@
+/*////////////////////////////////////////////////////////////////////////////\r
+ *  Project:\r
+ *    Memory_and_Exception_Trace\r
+ *\r
+ * ///////////////////////////////////////////////////////////////////////////\r
+ *     File:\r
+ *             Stackwalker.cpp\r
+ *\r
+ *     Remarks:\r
+ *    Dumps memory leaks (unreleased allocations) for CRT-Allocs and COM-Allocs\r
+ *    Dumps the stack of an thread if an exepction occurs\r
+ *\r
+ *  Known bugs:\r
+ *    - If the allocation-RequestID wrap, then allocations will get lost...\r
+ *\r
+ *     Author:\r
+ *             Jochen Kalmbach, Germany\r
+ *    (c) 2002-2005 (Freeware)\r
+ *    http://www.codeproject.com/tools/leakfinder.asp\r
+ * \r
+ * License (The zlib/libpng License, http://www.opensource.org/licenses/zlib-license.php):\r
+ *\r
+ * Copyright (c) 2005 Jochen Kalmbach\r
+ *\r
+ * This software is provided 'as-is', without any express or implied warranty. \r
+ * In no event will the authors be held liable for any damages arising from the \r
+ * use of this software.\r
+ *\r
+ * Permission is granted to anyone to use this software for any purpose, including \r
+ * commercial applications, and to alter it and redistribute it freely, subject to \r
+ * the following restrictions:\r
+ * \r
+ * 1. The origin of this software must not be misrepresented; you must not claim \r
+ *    that you wrote the original software. If you use this software in a product, \r
+ *    an acknowledgment in the product documentation would be appreciated but is \r
+ *    not required.\r
+ *\r
+ * 2. Altered source versions must be plainly marked as such, and must not be \r
+ *    misrepresented as being the original software.\r
+ *\r
+ * 3. This notice may not be removed or altered from any source distribution.\r
+ *\r
+ *//////////////////////////////////////////////////////////////////////////////\r
+\r
+//#include "stdafx.h"  // should be uncommented for precompiled headers\r
+\r
+#include <windows.h>\r
+#include <string>\r
+#include <vector>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <time.h>\r
+#include <crtdbg.h>\r
+#include <tchar.h>\r
+\r
+#include "Stackwalker.h"\r
+\r
+#pragma warning(push)\r
+#pragma warning(disable : 4100)\r
+#pragma warning(disable : 4996)\r
+#pragma warning(disable : 4189)\r
+#pragma warning(disable : 4245)\r
+#pragma warning(disable : 4701)\r
+\r
+// If the following is defined, only the used memories are stored in the hash-table. \r
+// If the memory is freed, it will be removed from the hash-table (to reduce memory)\r
+// Consequences: At DeInitAllocHook, only Leaks will be reported\r
+#define HASH_ENTRY_REMOVE_AT_FREE\r
+\r
+\r
+// 0 = Do not write any output during runtime-alloc-call\r
+// 1 = Write only the alloc action (malloc, realloc, free)\r
+// 2 = Write alloc action and callstack only for malloc/realloc\r
+// 3 = Write alloc action and callstack for all actions\r
+static ULONG g_ulShowStackAtAlloc = 0;\r
+\r
+// the form of the output file\r
+static eAllocCheckOutput g_CallstackOutputType = ACOutput_Simple;\r
+\r
+\r
+// Size of Hash-Table (this should be a prime number to avoid collisions)\r
+#define ALLOC_HASH_ENTRIES 1023\r
+\r
+\r
+// Size of Callstack-trace in bytes (0x500 => appr. 5-9 functions, depending on parameter count for each function)\r
+#define MAX_ESP_LEN_BUF 0x500\r
+\r
+\r
+// Normally we can ignore allocations from the Runtime-System\r
+#define IGNORE_CRT_ALLOC\r
+\r
+// MaxSize: 1 MByte (only for StackwalkFilter)\r
+#define LOG_FILE_MAX_SIZE 1024*1024\r
+\r
+// If the following is defined, then COM-Leaks will also be tracked\r
+#define WITH_IMALLOC_SPY\r
+\r
+\r
+// #############################################################################################\r
+#ifdef WITH_IMALLOC_SPY\r
+//forwards:\r
+void IMallocHashInsert(void *pData, CONTEXT &Context, size_t nDataSize);\r
+BOOL IMallocHashRemove(void *pData);\r
+\r
+// IMallocSpy-Interface\r
+class CMallocSpy : public IMallocSpy\r
+{\r
+public:\r
+  CMallocSpy(void) {\r
+    m_cbRequest = 0;\r
+  }\r
+  ~CMallocSpy(void) {\r
+  }\r
+  // IUnknown methods\r
+  STDMETHOD(QueryInterface) (REFIID riid, LPVOID *ppUnk) {\r
+    HRESULT hr = S_OK;\r
+    if (IsEqualIID(riid, IID_IUnknown)) {\r
+        *ppUnk = (IUnknown *) this;\r
+    }\r
+    else if (IsEqualIID(riid, IID_IMallocSpy)) {\r
+        *ppUnk =  (IMalloc *) this;\r
+    }\r
+    else {\r
+        *ppUnk = NULL;\r
+        hr =  E_NOINTERFACE;\r
+    }\r
+    AddRef();\r
+    return hr;\r
+  }\r
+  STDMETHOD_(ULONG, AddRef) (void) {\r
+    return InterlockedIncrement(&m_cRef);\r
+  }\r
+  STDMETHOD_(ULONG, Release) (void) {\r
+    LONG cRef;\r
+    cRef = InterlockedDecrement(&m_cRef);\r
+    if (cRef == 0)\r
+    {\r
+      delete this;\r
+    }\r
+    return cRef;\r
+  }\r
+  // IMallocSpy methods\r
+  STDMETHOD_(ULONG, PreAlloc) (ULONG cbRequest) {\r
+    m_cbRequest = cbRequest;\r
+    return cbRequest;\r
+  }\r
+  STDMETHOD_(void *, PostAlloc) (void *pActual) {\r
+    HANDLE hThread;\r
+    if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),\r
+      GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) != 0) {\r
+      // Ok\r
+      CONTEXT c;\r
+      memset( &c, '\0', sizeof c );\r
+      c.ContextFlags = CONTEXT_FULL;\r
+#if 0\r
+      if ( GetThreadContext(hThread, &c) != 0) {\r
+#else\r
+      __asm\r
+      {\r
+        call x\r
+        x: pop eax\r
+        mov c.Eip, eax\r
+        mov c.Ebp, ebp\r
+      }\r
+      {\r
+#endif\r
+        // Ok\r
+        IMallocHashInsert(pActual, c, m_cbRequest);\r
+      }\r
+      CloseHandle(hThread);\r
+    }\r
+    return pActual;\r
+  }\r
+  STDMETHOD_(void *, PreFree) (void *pRequest, BOOL fSpyed) {\r
+    IMallocHashRemove(pRequest);\r
+    return pRequest;\r
+  }\r
+  STDMETHOD_(void, PostFree) (BOOL fSpyed) {\r
+    return;\r
+  }\r
+  STDMETHOD_(ULONG, PreRealloc) (void *pRequest, ULONG cbRequest,\r
+    void **ppNewRequest, BOOL fSpyed) {\r
+    IMallocHashRemove(pRequest);\r
+    m_cbRequest = cbRequest;\r
+    *ppNewRequest = pRequest;  // Bug fixed. Thanx to Christoph Weber\r
+    return cbRequest;\r
+  }\r
+  STDMETHOD_(void *, PostRealloc) (void *pActual, BOOL fSpyed) {\r
+    HANDLE hThread;\r
+    if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),\r
+      GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) != 0) {\r
+      // Ok\r
+      CONTEXT c;\r
+      memset( &c, '\0', sizeof c );\r
+      c.ContextFlags = CONTEXT_FULL;\r
+#if 0\r
+      if ( GetThreadContext(hThread, &c) != 0) {\r
+#else\r
+      __asm\r
+      {\r
+        call x\r
+        x: pop eax\r
+        mov c.Eip, eax\r
+        mov c.Ebp, ebp\r
+      }\r
+      {\r
+#endif\r
+        // Ok\r
+        IMallocHashInsert(pActual, c, m_cbRequest);\r
+      }\r
+      CloseHandle(hThread);\r
+    }\r
+    return pActual;\r
+  }\r
+  STDMETHOD_(void *, PreGetSize) (void *pRequest, BOOL fSpyed) {\r
+    return pRequest;\r
+  }\r
+  STDMETHOD_(ULONG, PostGetSize) (ULONG cbActual, BOOL fSpyed) {\r
+    return cbActual;\r
+  }\r
+  STDMETHOD_(void *, PreDidAlloc) (void *pRequest, BOOL fSpyed) {\r
+    return pRequest;\r
+  }\r
+  STDMETHOD_(BOOL, PostDidAlloc) (void *pRequest, BOOL fSpyed, BOOL fActual) {\r
+    return fActual;\r
+  }\r
+  STDMETHOD_(void, PreHeapMinimize) (void) {\r
+    return;\r
+  }\r
+  STDMETHOD_(void, PostHeapMinimize) (void) {\r
+    return;\r
+  }\r
+private:\r
+  LONG    m_cRef;\r
+  ULONG m_cbRequest;\r
+};\r
+#endif\r
+\r
+// #############################################################################################\r
+// Here I have included the API-Version 9 declarations, so it will also compile on systems, where the new PSDK is not installed\r
+// Normally we just need to include the "dbghelp.h" file\r
+#include <imagehlp.h>\r
+#if API_VERSION_NUMBER < 9\r
+typedef\r
+BOOL\r
+(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(\r
+    HANDLE      hProcess,\r
+    DWORD64     qwBaseAddress,\r
+    PVOID       lpBuffer,\r
+    DWORD       nSize,\r
+    LPDWORD     lpNumberOfBytesRead\r
+    );\r
+\r
+typedef struct _IMAGEHLP_LINE64 {\r
+    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_LINE64)\r
+    PVOID                       Key;                    // internal\r
+    DWORD                       LineNumber;             // line number in file\r
+    PCHAR                       FileName;               // full filename\r
+    DWORD64                     Address;                // first instruction of line\r
+} IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;\r
+\r
+\r
+typedef struct _IMAGEHLP_MODULE64 {\r
+    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_MODULE64)\r
+    DWORD64                     BaseOfImage;            // base load address of module\r
+    DWORD                       ImageSize;              // virtual size of the loaded module\r
+    DWORD                       TimeDateStamp;          // date/time stamp from pe header\r
+    DWORD                       CheckSum;               // checksum from the pe header\r
+    DWORD                       NumSyms;                // number of symbols in the symbol table\r
+    SYM_TYPE                    SymType;                // type of symbols loaded\r
+    CHAR                        ModuleName[32];         // module name\r
+    CHAR                        ImageName[256];         // image name\r
+    CHAR                        LoadedImageName[256];   // symbol file name\r
+} IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;\r
+\r
+typedef struct _IMAGEHLP_SYMBOL64 {\r
+    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_SYMBOL64)\r
+    DWORD64                     Address;                // virtual address including dll base address\r
+    DWORD                       Size;                   // estimated size of symbol, can be zero\r
+    DWORD                       Flags;                  // info about the symbols, see the SYMF defines\r
+    DWORD                       MaxNameLength;          // maximum size of symbol name in 'Name'\r
+    CHAR                        Name[1];                // symbol name (null terminated string)\r
+} IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;\r
+\r
+typedef struct _tagADDRESS64 {\r
+    DWORD64       Offset;\r
+    WORD          Segment;\r
+    ADDRESS_MODE  Mode;\r
+} ADDRESS64, *LPADDRESS64;\r
+\r
+typedef struct _KDHELP64 {\r
+\r
+    //\r
+    // address of kernel thread object, as provided in the\r
+    // WAIT_STATE_CHANGE packet.\r
+    //\r
+    DWORD64   Thread;\r
+\r
+    //\r
+    // offset in thread object to pointer to the current callback frame\r
+    // in kernel stack.\r
+    //\r
+    DWORD   ThCallbackStack;\r
+\r
+    //\r
+    // offset in thread object to pointer to the current callback backing\r
+    // store frame in kernel stack.\r
+    //\r
+    DWORD   ThCallbackBStore;\r
+\r
+    //\r
+    // offsets to values in frame:\r
+    //\r
+    // address of next callback frame\r
+    DWORD   NextCallback;\r
+\r
+    // address of saved frame pointer (if applicable)\r
+    DWORD   FramePointer;\r
+\r
+\r
+    //\r
+    // Address of the kernel function that calls out to user mode\r
+    //\r
+    DWORD64   KiCallUserMode;\r
+\r
+    //\r
+    // Address of the user mode dispatcher function\r
+    //\r
+    DWORD64   KeUserCallbackDispatcher;\r
+\r
+    //\r
+    // Lowest kernel mode address\r
+    //\r
+    DWORD64   SystemRangeStart;\r
+\r
+    DWORD64  Reserved[8];\r
+\r
+} KDHELP64, *PKDHELP64;\r
+\r
+\r
+typedef struct _tagSTACKFRAME64 {\r
+    ADDRESS64   AddrPC;               // program counter\r
+    ADDRESS64   AddrReturn;           // return address\r
+    ADDRESS64   AddrFrame;            // frame pointer\r
+    ADDRESS64   AddrStack;            // stack pointer\r
+    ADDRESS64   AddrBStore;           // backing store pointer\r
+    PVOID       FuncTableEntry;       // pointer to pdata/fpo or NULL\r
+    DWORD64     Params[4];            // possible arguments to the function\r
+    BOOL        Far;                  // WOW far call\r
+    BOOL        Virtual;              // is this a virtual frame?\r
+    DWORD64     Reserved[3];\r
+    KDHELP64    KdHelp;\r
+} STACKFRAME64, *LPSTACKFRAME64;\r
+\r
+typedef\r
+PVOID\r
+(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)(\r
+    HANDLE  hProcess,\r
+    DWORD64 AddrBase\r
+    );\r
+\r
+typedef\r
+DWORD64\r
+(__stdcall *PGET_MODULE_BASE_ROUTINE64)(\r
+    HANDLE  hProcess,\r
+    DWORD64 Address\r
+    );\r
+\r
+typedef\r
+DWORD64\r
+(__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(\r
+    HANDLE    hProcess,\r
+    HANDLE    hThread,\r
+    LPADDRESS64 lpaddr\r
+    );\r
+#endif\r
+// #############################################################################################\r
+\r
+\r
+\r
+// Forward definitions of functions:\r
+static void ShowStackRM( HANDLE hThread, CONTEXT& c, FILE *fLogFile, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryFunction, HANDLE hProcess);\r
+static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile);\r
+\r
+//static void AllocHashOut(FILE*);\r
+static ULONG AllocHashOutLeaks(FILE*);\r
+\r
+\r
+\r
+// Globale Vars:\r
+static TCHAR *g_pszAllocLogName = NULL;\r
+static FILE *g_fFile = NULL;\r
+\r
+// AllocCheckFileOpen\r
+//  Checks if the log-file is already opened\r
+//  if not, try to open file (append or create if not exists)\r
+//  if open failed, redirect output to stdout\r
+static void AllocCheckFileOpen(bool bAppend = true) {\r
+  // is the File already open? If not open it...\r
+  if (g_fFile == NULL)\r
+    if (g_pszAllocLogName != NULL)\r
+    {\r
+      if (bAppend == false)\r
+        g_fFile = _tfopen(g_pszAllocLogName, _T("w"));\r
+      else\r
+        g_fFile = _tfopen(g_pszAllocLogName, _T("a"));\r
+    }\r
+  if (g_fFile == NULL)\r
+    g_fFile = stdout;\r
+}\r
+\r
+// Write Date/Time to specified file (will also work after 2038)\r
+static void WriteDateTime(FILE *fFile, BOOL asXMLAttrs = FALSE) {\r
+  TCHAR pszTemp[11], pszTemp2[11];\r
+\r
+  if (fFile != NULL) {\r
+    _tstrdate( pszTemp );\r
+    _tstrtime( pszTemp2 );\r
+    if (asXMLAttrs == FALSE)\r
+      _ftprintf(fFile,  _T("%s %s"), pszTemp, pszTemp2 );  // also ok after year 2038 (asctime is NOT ok)\r
+    else\r
+      _ftprintf(fFile,  _T("date=\"%s\" time=\"%s\" "), pszTemp, pszTemp2 );  // also ok after year 2038 (asctime is NOT ok)\r
+  }\r
+}  // WriteDateTime\r
+\r
+\r
+/*******************************************************************************\r
+ * Hash-Tabelle\r
+ *******************************************************************************/\r
+// Memory for the EIP-Address (is used by the ShowStack-method)\r
+#define MAX_EIP_LEN_BUF 4\r
+\r
+#define ALLOC_ENTRY_NOT_FOUND 0xFFFFFFFF\r
+\r
+typedef struct AllocHashEntryType {\r
+  long                       lRequestID;    // RequestID from CRT (if 0, then this entry is empty)\r
+  size_t                     nDataSize;     // Size of the allocated memory\r
+  char                       cRemovedFlag;  // 0 => memory was not yet released\r
+  struct AllocHashEntryType  *Next;\r
+  // Callstack for EIP\r
+  DWORD                      dwEIPOffset;\r
+  DWORD                      dwEIPLen;\r
+  char                       pcEIPAddr[MAX_EIP_LEN_BUF];\r
+  // Callstack for ESP\r
+  DWORD                      dwESPOffset;\r
+  DWORD                      dwESPLen;\r
+  char                       pcESPAddr[MAX_ESP_LEN_BUF];\r
+} AllocHashEntryType;\r
+\r
+static AllocHashEntryType AllocHashTable[ALLOC_HASH_ENTRIES];\r
+static ULONG AllocHashEntries = 0;\r
+static ULONG AllocHashCollisions = 0;\r
+static ULONG AllocHashFreed = 0;\r
+static ULONG AllocHashMaxUsed = 0; // maximal number of concurrent entries\r
+static ULONG AllocHashCurrentCount = 0;\r
+\r
+static ULONG AllocHashMaxCollisions = 0;\r
+static ULONG AllocHashCurrentCollisions = 0;\r
+\r
+// ##########################################################################################\r
+#ifdef WITH_IMALLOC_SPY\r
+// eigene Tabelle für die IMallocs:\r
+typedef struct IMallocHashEntryType {\r
+  void                       *pData;    // Key-Word\r
+  size_t                     nDataSize;     // größe des Datenblocks (optional)\r
+  char                       cRemovedFlag;  // 0 => nicht wurde noch nicht freigegeben\r
+  struct IMallocHashEntryType  *Next;\r
+  // Callstack für EIP\r
+  DWORD                      dwEIPOffset;\r
+  DWORD                      dwEIPLen;\r
+  char                       pcEIPAddr[MAX_EIP_LEN_BUF];\r
+  // Callstack für ESP\r
+  DWORD                      dwESPOffset;\r
+  DWORD                      dwESPLen;\r
+  char                       pcESPAddr[MAX_ESP_LEN_BUF];\r
+} IMallocHashEntryType;\r
+\r
+static IMallocHashEntryType IMallocHashTable[ALLOC_HASH_ENTRIES];\r
+\r
+static ULONG IMallocHashEntries = 0;\r
+static ULONG IMallocHashCollisions = 0;\r
+static ULONG IMallocHashFreed = 0;\r
+static ULONG IMallocHashMaxUsed = 0; // maximal number of concurrent entries\r
+static ULONG IMallocHashCurrentCount = 0;\r
+\r
+static ULONG IMallocHashMaxCollisions = 0;\r
+static ULONG IMallocHashCurrentCollisions = 0;\r
+\r
+\r
+//static void AllocHashOut(FILE*);\r
+static ULONG IMallocHashOutLeaks(FILE*);\r
+\r
+// AllocHashFunction\r
+//   Die eigentliche Hash-Funktion (hier ganz simpel)\r
+static ULONG IMallocHashFunction(void *pData) {\r
+  ULONG ulTemp;\r
+  DWORD dwPointer = (DWORD) pData;\r
+\r
+  // relativ simpler Mechanismus für die Hash-Funktion,\r
+  // mir ist nur nix besseres eingefallen...\r
+  ulTemp = dwPointer % ALLOC_HASH_ENTRIES;\r
+\r
+  _ASSERTE( (ulTemp >= 0) && (ulTemp < ALLOC_HASH_ENTRIES) );\r
+\r
+  return ulTemp;\r
+}  // AllocHashFunction\r
+\r
+// IMallocHashInsert\r
+//   pData: Key-Word (Pointer to address)\r
+//   pContext:   Context-Record, for retrieving Callstack (EIP and EBP is only needed)\r
+//   nDataSize:  How many bytes\r
+void IMallocHashInsert(void *pData, CONTEXT &Context, size_t nDataSize) {\r
+  ULONG HashIdx;\r
+  IMallocHashEntryType *pHashEntry;\r
+\r
+  // ermittle Statistische Werte\r
+  IMallocHashEntries++;\r
+  IMallocHashCurrentCount++;\r
+  if (IMallocHashCurrentCount > IMallocHashMaxUsed)\r
+    IMallocHashMaxUsed = IMallocHashCurrentCount;\r
+\r
+  // ermittle den Hash-Wert\r
+  HashIdx = IMallocHashFunction(pData);\r
+\r
+  // Eintrag darf nicht größer als die Hash-Tabelle sein\r
+  _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);\r
+\r
+  pHashEntry = &IMallocHashTable[HashIdx];\r
+  if (pHashEntry->pData == 0) {\r
+    // es ist noch kein Eintrag da\r
+  }\r
+  else {\r
+    //Statistische Daten:\r
+    IMallocHashCollisions++;\r
+    IMallocHashCurrentCollisions++;\r
+    if (IMallocHashCurrentCollisions > IMallocHashMaxCollisions)\r
+      IMallocHashMaxCollisions = IMallocHashCurrentCollisions;\r
+\r
+    // Eintrag ist schon belegt, verkette die Einträge\r
+    // wenn dies oft vorkommt, sollte man entweder die Tabelle vergrößern oder eine\r
+    // andere Hash-Funktion wählen\r
+    while(pHashEntry->Next != NULL) {\r
+      pHashEntry = pHashEntry->Next;\r
+    }\r
+\r
+    pHashEntry->Next = (IMallocHashEntryType*) _calloc_dbg(sizeof(IMallocHashEntryType), 1, _CRT_BLOCK, __FILE__, __LINE__);\r
+    pHashEntry = pHashEntry->Next;\r
+\r
+  }\r
+  pHashEntry->pData = pData;  // Key-Word\r
+  pHashEntry->nDataSize = nDataSize;\r
+  pHashEntry->Next = NULL;\r
+  // Get EIP and save it in the record\r
+  pHashEntry->dwEIPOffset = Context.Eip;\r
+  if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {\r
+    // Could not read memory... remove everything...\r
+    memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);\r
+    pHashEntry->dwEIPLen = 0;\r
+    pHashEntry->dwEIPOffset = 0;\r
+  }\r
+\r
+  // Get ESP and save it in the record\r
+  pHashEntry->dwESPOffset = Context.Ebp;\r
+  if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {\r
+    // Could not read memory... remove everything...\r
+    memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);\r
+    pHashEntry->dwESPLen = 0;\r
+    pHashEntry->dwESPOffset = 0;\r
+\r
+    // Check if I tried to read too much...\r
+    if (GetLastError() == ERROR_PARTIAL_COPY)\r
+    {\r
+      // ask how many I can read:\r
+      MEMORY_BASIC_INFORMATION MemBuffer;\r
+      DWORD dwRet = VirtualQuery((LPCVOID) Context.Ebp, &MemBuffer, sizeof(MemBuffer));\r
+      if (dwRet > 0)\r
+      {\r
+        // calculate the length\r
+        DWORD len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;\r
+        if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )\r
+        {\r
+          // try to read it again (with the shorter length)\r
+          if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)\r
+          {\r
+            // ok, now everything goes wrong... remove it...\r
+            memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);\r
+            pHashEntry->dwESPLen = 0;\r
+            pHashEntry->dwESPOffset = 0;\r
+          }\r
+          else\r
+          {\r
+            pHashEntry->dwESPOffset = Context.Ebp;\r
+          }\r
+        }\r
+      } // VirtualQuery was successfully\r
+    }  // ERROR_PARTIAL_COPY\r
+  }\r
+}\r
+\r
+// IMallocHashFind\r
+//   Wird ALLOC_ENTRY_NOT_FOUND zurückgegeben, so wurde der Key nicht \r
+//   gefunden, ansonsten wird ein Zeiger auf den Hash-Eintrag zurückgegeben\r
+//   ACHTUNG: In einem preemptiven Tasking-System kann hier nicht \r
+//            garantiert werden, ob der Zeiger noch gültig ist, wenn er \r
+//            zurückgegeben wird, da er von einem anderen Thread schon\r
+//            freigegeben sein könnte. \r
+//            Die synchronisation muß eine Ebene höher erfolgen\r
+static IMallocHashEntryType *IMallocHashFind(void *pData) {\r
+  ULONG HashIdx;\r
+  IMallocHashEntryType *pHashEntry;\r
+\r
+  // ermittle den Hash-Wert\r
+  HashIdx = IMallocHashFunction(pData);\r
+\r
+  // Eintrag darf nicht größer als die Hash-Tabelle sein\r
+  _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);\r
+\r
+  pHashEntry = &IMallocHashTable[HashIdx];\r
+  while(pHashEntry != NULL) {\r
+    if (pHashEntry->pData == pData) {\r
+      return pHashEntry;\r
+    }\r
+    pHashEntry = pHashEntry->Next;\r
+  }\r
+\r
+  // wenn hier angelangt, dann wurde der Eintrag nicht gefunden!\r
+  return (IMallocHashEntryType*) ALLOC_ENTRY_NOT_FOUND;\r
+}  // AllocHashFind\r
+\r
+// IMallocHashRemove\r
+//   Return: FALSE (0) : Key wurde gefunden und entfernt/markiert\r
+//           TRUE (!=0): Key wurde nicht gefunden!\r
+BOOL IMallocHashRemove(void *pData) {\r
+  ULONG HashIdx;\r
+  IMallocHashEntryType *pHashEntry, *pHashEntryLast;\r
+\r
+  // ermittle den Hash-Wert\r
+  HashIdx = IMallocHashFunction(pData);\r
+\r
+  // Eintrag darf nicht größer als die Hash-Tabelle sein\r
+  _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);\r
+\r
+  pHashEntryLast = NULL;\r
+  pHashEntry = &IMallocHashTable[HashIdx];\r
+  while(pHashEntry != NULL) {\r
+    if (pHashEntry->pData == pData) {\r
+#ifdef HASH_ENTRY_REMOVE_AT_FREE\r
+      IMallocHashFreed++;\r
+      IMallocHashCurrentCount--;\r
+      // gebe den Speicher frei\r
+      if (pHashEntryLast == NULL) {\r
+        // Es ist ein Eintrag direkt in der Tabelle\r
+        if (pHashEntry->Next == NULL) {\r
+          // Es ist der letze Eintrag lösche also die Tabelle\r
+          memset(&IMallocHashTable[HashIdx], 0, sizeof(IMallocHashTable[HashIdx]));\r
+        }\r
+        else {\r
+          // Es sind noch Einträge verkettet, Ã¼berschreibe einfach den nicht mehr gebrauchten...\r
+          IMallocHashEntryType *pTmp = pHashEntry->Next;\r
+          *pHashEntry = *(pHashEntry->Next);\r
+          _free_dbg(pTmp, _CRT_BLOCK);\r
+        }\r
+        return TRUE;\r
+      }\r
+      else {\r
+        // ich bin in einem dynamischen Bereich\r
+        // dies war eine kollisions, zähle also wieder zurück:\r
+        IMallocHashCurrentCollisions--;\r
+        pHashEntryLast->Next = pHashEntry->Next;\r
+        _free_dbg(pHashEntry, _CRT_BLOCK);\r
+        return TRUE;\r
+      }\r
+#else\r
+      // erhöhe nur den Removed counter und behalte das Object im Speicher\r
+      pHashEntry->cRemovedFlag++;\r
+      return TRUE;  // erfolgreich\r
+#endif\r
+    }\r
+    pHashEntryLast = pHashEntry;\r
+    pHashEntry = pHashEntry->Next;\r
+  }\r
+\r
+  // wenn hier angelangt, dann wurde der Eintrag nicht gefunden!\r
+  return FALSE;\r
+}\r
+\r
+\r
+\r
+//   Callback-Funtion for StackWalk für meine CallStack-Ausgabe aus der Hash-Tabelle\r
+static BOOL __stdcall ReadProcMemoryFromIMallocHash(HANDLE pData, DWORD64 lpBaseAddress, PVOID lpBuffer, DWORD nSize, PDWORD lpNumberOfBytesRead) {\r
+  // Versuche die hRequestID zu finden\r
+  IMallocHashEntryType *pHashEntry;\r
+  *lpNumberOfBytesRead = 0;\r
+\r
+  pHashEntry = IMallocHashFind((PVOID) pData);\r
+  if (pHashEntry == (IMallocHashEntryType*) ALLOC_ENTRY_NOT_FOUND) {\r
+    // nicht gefunden, somit kann ich den Speicher nicht lesen\r
+    *lpNumberOfBytesRead = 0;\r
+    return FALSE;\r
+  }\r
+  if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {\r
+    // Speicher liegt im ESP:\r
+    // Errechne den Offset\r
+    DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;\r
+    DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);\r
+    memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);\r
+    *lpNumberOfBytesRead = dwSize;\r
+    if (dwSize != nSize)\r
+      return FALSE;\r
+  }\r
+\r
+  if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {\r
+    // Speicher liegt im EIP:\r
+    // Errechne den Offset\r
+    DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;\r
+    DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);\r
+    memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);\r
+    *lpNumberOfBytesRead = dwSize;\r
+    if (dwSize != nSize)\r
+      return FALSE;\r
+  }\r
+  \r
+  if (*lpNumberOfBytesRead == 0)  // Der Speicher konnte nicht gefunden werden\r
+    return FALSE;\r
+\r
+  return TRUE;\r
+}\r
+// AllocHashOutLeaks\r
+// Gibt allen Speicher aus, der noch nicht wieder freigegeben wurde\r
+//   Returns the number of bytes, that are not freed (leaks)\r
+ULONG IMallocHashOutLeaks(FILE *fFile) {\r
+  ULONG ulTemp;\r
+  IMallocHashEntryType *pHashEntry;\r
+  ULONG ulCount = 0;\r
+  ULONG ulLeaksByte = 0;\r
+\r
+  // Gehe jeden Eintrag durch und gebe ihn aus\r
+  for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {\r
+    pHashEntry = &IMallocHashTable[ulTemp];\r
+    if (pHashEntry->pData != 0) {\r
+      while(pHashEntry != NULL) {\r
+        // gebe die Zeile aus\r
+        if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {\r
+          ulCount++;\r
+          if (g_CallstackOutputType == ACOutput_XML)\r
+            _ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->pData, pHashEntry->nDataSize);\r
+          else\r
+            _ftprintf(fFile, _T("Pointer (RequestID): %12i, Removed: %i, Size: %12i\n"), pHashEntry->pData, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);\r
+          CONTEXT c;\r
+          memset( &c, '\0', sizeof c );\r
+          c.Eip = pHashEntry->dwEIPOffset;\r
+          c.Ebp = pHashEntry->dwESPOffset;\r
+          ShowStackRM( NULL, c, fFile, &ReadProcMemoryFromIMallocHash, (HANDLE) pHashEntry->pData);\r
+          // Zähle zusammen wieviel Byte noch nicht freigegeben wurden\r
+          if (pHashEntry->nDataSize > 0)\r
+            ulLeaksByte += pHashEntry->nDataSize;\r
+          else\r
+            ulLeaksByte++;  // Wenn zwar Speicher allokiert wurde, dieser aber 0 Bytes lang war, so reserviere für diesen zumindest 1 Byte\r
+\r
+          if (g_CallstackOutputType == ACOutput_XML)\r
+            _ftprintf(fFile, _T("</LEAK>\n"));  // terminate the xml-node\r
+        }\r
+        pHashEntry = pHashEntry->Next;\r
+      }\r
+    }\r
+  }\r
+  if (g_CallstackOutputType != ACOutput_XML)\r
+    _ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);\r
+  return ulLeaksByte;\r
+}  // AllocHashOutLeaks\r
+#endif\r
+\r
+\r
+static void AllocHashInit(void) {\r
+\r
+  memset(AllocHashTable, 0, sizeof(AllocHashTable));\r
+  AllocHashEntries = 0;\r
+  AllocHashCollisions = 0;\r
+  AllocHashFreed = 0;\r
+  AllocHashCurrentCount = 0;\r
+  AllocHashMaxUsed = 0;\r
+\r
+  AllocHashMaxCollisions = 0;\r
+  AllocHashCurrentCollisions = 0;\r
+\r
+#ifdef WITH_IMALLOC_SPY\r
+  memset(IMallocHashTable, 0, sizeof(IMallocHashTable));\r
+  IMallocHashEntries = 0;\r
+  IMallocHashCollisions = 0;\r
+  IMallocHashFreed = 0;\r
+  IMallocHashCurrentCount = 0;\r
+  IMallocHashMaxUsed = 0;\r
+\r
+  IMallocHashMaxCollisions = 0;\r
+  IMallocHashCurrentCollisions = 0;\r
+#endif\r
+  return;\r
+}  // AllocHashInit\r
+\r
+\r
+// AllocHashDeinit\r
+// Returns the number of bytes, that are not freed (leaks)\r
+static ULONG AllocHashDeinit(void) {\r
+  ULONG ulRet = 0;\r
+  bool bAppend = g_CallstackOutputType != ACOutput_XML;\r
+  AllocCheckFileOpen(false);//bAppend);  // open global log-file\r
+\r
+  if (g_CallstackOutputType == ACOutput_XML)\r
+  {\r
+    _ftprintf(g_fFile, _T("<MEMREPORT "));\r
+    WriteDateTime(g_fFile, TRUE);\r
+    _ftprintf(g_fFile, _T(">\n"));\r
+  }\r
+  else\r
+  {\r
+    _ftprintf(g_fFile, _T("\n##### Memory Report ########################################\n"));\r
+    WriteDateTime(g_fFile);\r
+    _ftprintf(g_fFile, _T("\n"));\r
+  }\r
+\r
+#ifndef HASH_ENTRY_REMOVE_AT_FREE\r
+  // output the used memory\r
+  if (g_CallstackOutputType != ACOutput_XML)\r
+    _ftprintf(g_fFile, _T("##### Memory used: #########################################\n"));\r
+  AllocHashOut(g_fFile);\r
+#endif\r
+\r
+  // output the Memoty leaks\r
+  if (g_CallstackOutputType != ACOutput_XML)\r
+    _ftprintf(g_fFile, _T("\n##### Leaks: ###############################################\n"));\r
+  ulRet = AllocHashOutLeaks(g_fFile);\r
+\r
+  if (g_CallstackOutputType == ACOutput_Advanced)\r
+  {\r
+    // output some statistics from the hash-table\r
+    _ftprintf(g_fFile, _T("***** Hash-Table statistics:\n"));\r
+    _ftprintf(g_fFile, _T("      Table-Size:     %i\n"), ALLOC_HASH_ENTRIES);\r
+    _ftprintf(g_fFile, _T("      Inserts:        %i\n"), AllocHashEntries);\r
+    _ftprintf(g_fFile, _T("      Freed:          %i\n"), AllocHashFreed);\r
+    _ftprintf(g_fFile, _T("      Sum Collisions: %i\n"), AllocHashCollisions);\r
+    _ftprintf(g_fFile, _T("\n"));\r
+    _ftprintf(g_fFile, _T("      Max used:       %i\n"), AllocHashMaxUsed);\r
+    _ftprintf(g_fFile, _T("      Max Collisions: %i\n"), AllocHashMaxCollisions);\r
+  }\r
+\r
+  // Free Hash-Table\r
+  ULONG ulTemp;\r
+  AllocHashEntryType *pHashEntry, *pHashEntryOld;\r
+\r
+  // Now, free my own memory\r
+  for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {\r
+    pHashEntry = &AllocHashTable[ulTemp];\r
+    while(pHashEntry != NULL) {\r
+      pHashEntryOld = pHashEntry;\r
+      pHashEntry = pHashEntry->Next;\r
+      if (pHashEntryOld != &AllocHashTable[ulTemp]) {\r
+        // now free the dynamically allocated memory\r
+        free(pHashEntryOld);\r
+      }\r
+    }  // while\r
+  }  // for\r
+  // empty the hash-table\r
+  memset(AllocHashTable, 0, sizeof(AllocHashTable));\r
+\r
+#ifdef WITH_IMALLOC_SPY\r
+  // output the Memoty leaks\r
+  if (g_CallstackOutputType != ACOutput_XML)\r
+    _ftprintf(g_fFile, _T("\n##### COM-Leaks: ###############################################\n"));\r
+  ulRet = IMallocHashOutLeaks(g_fFile);\r
+\r
+  if (g_CallstackOutputType == ACOutput_Advanced)\r
+  {\r
+    // output some statistics from the hash-table\r
+    _ftprintf(g_fFile, _T("***** Hash-Table statistics:\n"));\r
+    _ftprintf(g_fFile, _T("      Table-Size:     %i\n"), ALLOC_HASH_ENTRIES);\r
+    _ftprintf(g_fFile, _T("      Inserts:        %i\n"), IMallocHashEntries);\r
+    _ftprintf(g_fFile, _T("      Freed:          %i\n"), IMallocHashFreed);\r
+    _ftprintf(g_fFile, _T("      Sum Collisions: %i\n"), IMallocHashCollisions);\r
+    _ftprintf(g_fFile, _T("\n"));\r
+    _ftprintf(g_fFile, _T("      Max used:       %i\n"), IMallocHashMaxUsed);\r
+    _ftprintf(g_fFile, _T("      Max Collisions: %i\n"), IMallocHashMaxCollisions);\r
+  }\r
+\r
+  // Free Hash-Table\r
+  //ULONG ulTemp;\r
+  IMallocHashEntryType *pIMHashEntry, *pIMHashEntryOld;\r
+\r
+  // Gehe jeden Eintrag durch und gebe ihn frei\r
+  for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {\r
+    pIMHashEntry = &IMallocHashTable[ulTemp];\r
+    while(pHashEntry != NULL) {\r
+      pIMHashEntryOld = pIMHashEntry;\r
+      pIMHashEntry = pIMHashEntry->Next;\r
+      if (pIMHashEntryOld != &IMallocHashTable[ulTemp]) {\r
+        // es ist dynamischer Speicher, gebe ihn also frei:\r
+        _free_dbg(pIMHashEntryOld, _CRT_BLOCK);\r
+      }\r
+    }  // while\r
+  }  // for\r
+  // Lösche die gesamte Hash-Tabelle\r
+  memset(IMallocHashTable, 0, sizeof(IMallocHashTable));\r
+#endif\r
+\r
+\r
+  if (g_CallstackOutputType == ACOutput_XML)\r
+    _ftprintf(g_fFile, _T("</MEMREPORT>\n"));\r
+\r
+  return ulRet;\r
+}  // AllocHashDeinit\r
+\r
+// AllocHashFunction\r
+// The has-function (very simple)\r
+static inline ULONG AllocHashFunction(long lRequestID) {\r
+  // I couldn´t find any better and faster\r
+  return lRequestID % ALLOC_HASH_ENTRIES;\r
+}  // AllocHashFunction\r
+\r
+// AllocHashInsert\r
+//   lRequestID: Key-Word (RequestID from AllocHook)\r
+//   pContext:   Context-Record, for retrieving Callstack (EIP and EBP is only needed)\r
+//   nDataSize:  How many bytes\r
+static void AllocHashInsert(long lRequestID, CONTEXT &Context, size_t nDataSize) {\r
+  ULONG HashIdx;\r
+  AllocHashEntryType *pHashEntry;\r
+\r
+  // change statistical data\r
+  AllocHashEntries++;\r
+  AllocHashCurrentCount++;\r
+  if (AllocHashCurrentCount > AllocHashMaxUsed)\r
+    AllocHashMaxUsed = AllocHashCurrentCount;\r
+\r
+  // generate hash-value\r
+  HashIdx = AllocHashFunction(lRequestID);\r
+\r
+  pHashEntry = &AllocHashTable[HashIdx];\r
+  if (pHashEntry->lRequestID == 0) {\r
+    // Entry is empty...\r
+  }\r
+  else {\r
+    // Entry is not empy! make a list of entries for this hash value...\r
+    // change statistical data\r
+    // if this happens often, you should increase the hah size or change the heash-function; \r
+    // to fasten the allocation time\r
+    AllocHashCollisions++;\r
+    AllocHashCurrentCollisions++;\r
+    if (AllocHashCurrentCollisions > AllocHashMaxCollisions)\r
+      AllocHashMaxCollisions = AllocHashCurrentCollisions;\r
+\r
+    while(pHashEntry->Next != NULL) {\r
+      pHashEntry = pHashEntry->Next;\r
+    }\r
+\r
+    pHashEntry->Next = (AllocHashEntryType*) calloc(sizeof(AllocHashEntryType), 1);\r
+    pHashEntry = pHashEntry->Next;\r
+\r
+  }\r
+  pHashEntry->lRequestID = lRequestID;  // Key-Word\r
+  pHashEntry->nDataSize = nDataSize;\r
+  pHashEntry->Next = NULL;\r
+  // Get EIP and save it in the record\r
+  pHashEntry->dwEIPOffset = Context.Eip;\r
+  if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {\r
+    // Could not read memory... remove everything...\r
+    memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);\r
+    pHashEntry->dwEIPLen = 0;\r
+    pHashEntry->dwEIPOffset = 0;\r
+  }\r
+\r
+  // Get ESP and save it in the record\r
+  pHashEntry->dwESPOffset = Context.Ebp;\r
+  if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {\r
+    // Could not read memory... remove everything...\r
+    memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);\r
+    pHashEntry->dwESPLen = 0;\r
+    pHashEntry->dwESPOffset = 0;\r
+\r
+    // Check if I tried to read too much...\r
+    if (GetLastError() == ERROR_PARTIAL_COPY)\r
+    {\r
+      // ask how many I can read:\r
+      MEMORY_BASIC_INFORMATION MemBuffer;\r
+      DWORD dwRet = VirtualQuery((LPCVOID) Context.Ebp, &MemBuffer, sizeof(MemBuffer));\r
+      if (dwRet > 0)\r
+      {\r
+        // calculate the length\r
+        DWORD len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;\r
+        if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )\r
+        {\r
+          // try to read it again (with the shorter length)\r
+          if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)\r
+          {\r
+            // ok, now everything goes wrong... remove it...\r
+            memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);\r
+            pHashEntry->dwESPLen = 0;\r
+            pHashEntry->dwESPOffset = 0;\r
+          }\r
+          else\r
+          {\r
+            pHashEntry->dwESPOffset = Context.Ebp;\r
+          }\r
+        }\r
+      } // VirtualQuery was successfully\r
+    }  // ERROR_PARTIAL_COPY\r
+  }\r
+}\r
+\r
+// AllocHashFind\r
+//   If ALLOC_ENTRY_NOT_FOUND is returned, the Key was not found!\r
+//   If the Key was found, a pointer to the entry is returned\r
+static AllocHashEntryType *AllocHashFind(long lRequestID) {\r
+  ULONG HashIdx;\r
+  AllocHashEntryType *pHashEntry;\r
+\r
+  // get the Hash-Value\r
+  HashIdx = AllocHashFunction(lRequestID);\r
+\r
+  // Just do some simple checks:\r
+  _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);\r
+\r
+  pHashEntry = &AllocHashTable[HashIdx];\r
+  while(pHashEntry != NULL) {\r
+    if (pHashEntry->lRequestID == lRequestID) {\r
+      return pHashEntry;\r
+    }\r
+    pHashEntry = pHashEntry->Next;\r
+  }\r
+\r
+  // entry was not found!\r
+  return (AllocHashEntryType*) ALLOC_ENTRY_NOT_FOUND;\r
+}  // AllocHashFind\r
+\r
+// AllocHashRemove\r
+//   Return: FALSE (0) : Key was found and removed/marked\r
+//           TRUE (!=0): Key was not found\r
+static BOOL AllocHashRemove(long lRequestID) {\r
+  ULONG HashIdx;\r
+  AllocHashEntryType *pHashEntry, *pHashEntryLast;\r
+\r
+  // get the Hash-Value\r
+  HashIdx = AllocHashFunction(lRequestID);\r
+\r
+  // Just do some simple checks:\r
+  _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);\r
+\r
+  pHashEntryLast = NULL;\r
+  pHashEntry = &AllocHashTable[HashIdx];\r
+  while(pHashEntry != NULL) {\r
+    if (pHashEntry->lRequestID == lRequestID) {\r
+#ifdef HASH_ENTRY_REMOVE_AT_FREE\r
+      AllocHashFreed++;\r
+      AllocHashCurrentCount--;\r
+      // release my memory\r
+      if (pHashEntryLast == NULL) {\r
+        // It is an entry in the table, so do not release this memory\r
+        if (pHashEntry->Next == NULL) {\r
+          // It was the last entry, so empty the table entry\r
+          memset(&AllocHashTable[HashIdx], 0, sizeof(AllocHashTable[HashIdx]));\r
+        }\r
+        else {\r
+          // There are some more entries, so shorten the list\r
+          AllocHashEntryType *pTmp = pHashEntry->Next;\r
+          *pHashEntry = *(pHashEntry->Next);\r
+          free(pTmp);\r
+        }\r
+        return TRUE;\r
+      }\r
+      else {\r
+        // now, I am in an dynamic allocated entry\r
+        // it was a collision, so decrease the current collision count\r
+        AllocHashCurrentCollisions--;\r
+        pHashEntryLast->Next = pHashEntry->Next;\r
+        free(pHashEntry);\r
+        return TRUE;\r
+      }\r
+#else\r
+      // increase the Remove-Count and let the objet stay in memory\r
+      pHashEntry->cRemovedFlag++;\r
+      return TRUE;\r
+#endif\r
+    }\r
+    pHashEntryLast = pHashEntry;\r
+    pHashEntry = pHashEntry->Next;\r
+  }\r
+\r
+  // if we are here, we could not find the RequestID\r
+  return FALSE;\r
+}\r
+\r
+// ReadProcMemoryFromHash\r
+//   Callback-Funtion for StackWalk for my own CallStack from the Hash-Table-Entries\r
+static BOOL __stdcall ReadProcMemoryFromHash(HANDLE hRequestID, DWORD64 lpBaseAddress, PVOID lpBuffer, DWORD nSize, PDWORD lpNumberOfBytesRead) {\r
+  // Try to find the RequestID\r
+  AllocHashEntryType *pHashEntry;\r
+  *lpNumberOfBytesRead = 0;\r
+\r
+  pHashEntry = AllocHashFind((LONG) hRequestID);\r
+  if (pHashEntry == (AllocHashEntryType*) ALLOC_ENTRY_NOT_FOUND) {\r
+    // Not found, so I cannot return any memory\r
+    *lpNumberOfBytesRead = 0;\r
+    return FALSE;\r
+  }\r
+  if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {\r
+    // Memory is located in ESP:\r
+    // Calculate the offset\r
+    DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;\r
+    DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);\r
+    memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);\r
+    *lpNumberOfBytesRead = dwSize;\r
+    if (dwSize != nSize)\r
+      return FALSE;\r
+  }\r
+\r
+  if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {\r
+    // Memory is located in EIP:\r
+    // Calculate the offset\r
+    DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;\r
+    DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);\r
+    memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);\r
+    *lpNumberOfBytesRead = dwSize;\r
+    if (dwSize != nSize)\r
+      return FALSE;\r
+  }\r
+  \r
+  if (*lpNumberOfBytesRead == 0)  // Memory could not be found\r
+    return FALSE;\r
+\r
+  return TRUE;\r
+}\r
+\r
+// AllocHashOutLeaks\r
+// Write all Memory (with callstack) which was not freed yet\r
+//   Returns the number of bytes, that are not freed (leaks)\r
+ULONG AllocHashOutLeaks(FILE *fFile) {\r
+  ULONG ulTemp;\r
+  AllocHashEntryType *pHashEntry;\r
+  ULONG ulCount = 0;\r
+  ULONG ulLeaksByte = 0;\r
+\r
+  // Move throu every entry\r
+  for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {\r
+    pHashEntry = &AllocHashTable[ulTemp];\r
+    if (pHashEntry->lRequestID != 0) {\r
+      while(pHashEntry != NULL) {\r
+        if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {\r
+          ulCount++;\r
+          if (g_CallstackOutputType == ACOutput_XML)\r
+            _ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->lRequestID, pHashEntry->nDataSize);\r
+          else\r
+            _ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);\r
+          CONTEXT c;\r
+          memset( &c, '\0', sizeof c );\r
+          c.Eip = pHashEntry->dwEIPOffset;\r
+          c.Ebp = pHashEntry->dwESPOffset;\r
+          ShowStackRM( NULL, c, fFile, &ReadProcMemoryFromHash, (HANDLE) pHashEntry->lRequestID);\r
+          // Count the number of leaky bytes\r
+          if (pHashEntry->nDataSize > 0)\r
+            ulLeaksByte += pHashEntry->nDataSize;\r
+          else\r
+            ulLeaksByte++;  // If memory was allocated with zero bytes, then just increase the counter 1\r
+\r
+          if (g_CallstackOutputType == ACOutput_XML)\r
+            _ftprintf(fFile, _T("</LEAK>\n"));  // terminate the xml-node\r
+        }\r
+        pHashEntry = pHashEntry->Next;\r
+      }\r
+    }\r
+  }\r
+  if (g_CallstackOutputType != ACOutput_XML)\r
+    _ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);\r
+  return ulLeaksByte;\r
+}  // AllocHashOutLeaks\r
+\r
+// Write all used memory to a file\r
+void AllocHashOut(FILE *fFile) {\r
+  ULONG ulTemp;\r
+  AllocHashEntryType *pHashEntry;\r
+\r
+  for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {\r
+    pHashEntry = &AllocHashTable[ulTemp];\r
+    if (pHashEntry->lRequestID != 0) {\r
+      while(pHashEntry != NULL) {\r
+        if (g_CallstackOutputType == ACOutput_XML)\r
+          _ftprintf(fFile, _T("<MEMUSED requestID=\"%u\" size=\"%u\"\n"), pHashEntry->lRequestID, pHashEntry->nDataSize);\r
+        else\r
+          _ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);\r
+        pHashEntry = pHashEntry->Next;\r
+      }\r
+    }\r
+  }\r
+}  // AllocHashOut\r
+\r
+/*******************************************************************************\r
+ * Ende der Hash-Tabelle\r
+ *******************************************************************************/\r
+\r
+\r
+// The follwoing is copied from dbgint.h:\r
+// <CRT_INTERNALS>\r
+/*\r
+ * For diagnostic purpose, blocks are allocated with extra information and\r
+ * stored in a doubly-linked list.  This makes all blocks registered with\r
+ * how big they are, when they were allocated, and what they are used for.\r
+ */\r
+\r
+#define nNoMansLandSize 4\r
+\r
+typedef struct _CrtMemBlockHeader\r
+{\r
+        struct _CrtMemBlockHeader * pBlockHeaderNext;\r
+        struct _CrtMemBlockHeader * pBlockHeaderPrev;\r
+        char *                      szFileName;\r
+        int                         nLine;\r
+#ifdef _WIN64\r
+        /* These items are reversed on Win64 to eliminate gaps in the struct\r
+         * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is\r
+         * maintained in the debug heap.\r
+         */\r
+        int                         nBlockUse;\r
+        size_t                      nDataSize;\r
+#else  /* _WIN64 */\r
+        size_t                      nDataSize;\r
+        int                         nBlockUse;\r
+#endif  /* _WIN64 */\r
+        long                        lRequest;\r
+        unsigned char               gap[nNoMansLandSize];\r
+        /* followed by:\r
+         *  unsigned char           data[nDataSize];\r
+         *  unsigned char           anotherGap[nNoMansLandSize];\r
+         */\r
+} _CrtMemBlockHeader;\r
+#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))\r
+#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)\r
\r
+// </CRT_INTERNALS>\r
+\r
+\r
+\r
+\r
+// Global data:\r
+static BOOL g_bInitialized = FALSE;\r
+static HINSTANCE g_hImagehlpDll = NULL;\r
+\r
+static DWORD g_dwShowCount = 0;  // increase at every ShowStack-Call\r
+static CRITICAL_SECTION g_csFileOpenClose = {0};\r
+\r
+// Is used for syncronising call to MyAllocHook (to prevent reentrant calls)\r
+static LONG g_lMallocCalled = 0;\r
+\r
+static _CRT_ALLOC_HOOK pfnOldCrtAllocHook = NULL;\r
+\r
+// Deaktivate AllocHook, by increasing the Syncronisation-Counter\r
+//static void DeactivateMallocStackwalker(void) {\r
+//  InterlockedIncrement(&g_lMallocCalled);\r
+//}\r
+\r
+\r
+// MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function!\r
+// Special case for VC 5\r
+#if _MSC_VER <= 1100 \r
+static int MyAllocHook(int nAllocType, void *pvData, \r
+      size_t nSize, int nBlockUse, long lRequest, \r
+      const char * szFileName, int nLine ) {\r
+#else\r
+static int MyAllocHook(int nAllocType, void *pvData, \r
+      size_t nSize, int nBlockUse, long lRequest, \r
+      const unsigned char * szFileName, int nLine ) {\r
+#endif\r
+  static TCHAR *operation[] = { _T(""), _T("ALLOCATIONG"), _T("RE-ALLOCATING"), _T("FREEING") };\r
+  static TCHAR *blockType[] = { _T("Free"), _T("Normal"), _T("CRT"), _T("Ignore"), _T("Client") };\r
+\r
+#ifdef IGNORE_CRT_ALLOC\r
+  if (_BLOCK_TYPE(nBlockUse) == _CRT_BLOCK)  // Ignore internal C runtime library allocations\r
+    return TRUE;\r
+#endif\r
+  extern int _crtDbgFlag;\r
+  if  ( ((_CRTDBG_ALLOC_MEM_DF & _crtDbgFlag) == 0) && ( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) ) )\r
+  {\r
+    // Someone has disabled that the runtime should log this allocation\r
+    // so we do not log this allocation\r
+    if (pfnOldCrtAllocHook != NULL)\r
+      pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);\r
+    return TRUE;\r
+  }\r
+\r
+  // Prevent from reentrat calls\r
+  if (InterlockedIncrement(&g_lMallocCalled) > 1) { // I was already called\r
+    InterlockedDecrement(&g_lMallocCalled);\r
+    // call the previous alloc hook\r
+    if (pfnOldCrtAllocHook != NULL)\r
+      pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);\r
+    return TRUE;\r
+  }\r
+\r
+  if (g_ulShowStackAtAlloc > 0) {\r
+    AllocCheckFileOpen();  // Open logfile\r
+  }\r
+\r
+   _ASSERT( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) || (nAllocType == _HOOK_FREE) );\r
+   _ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) );\r
+\r
+  if (nAllocType == _HOOK_FREE) { // freeing\r
+    // Try to get the header information\r
+    if (_CrtIsValidHeapPointer(pvData)) {  // it is a valid Heap-Pointer\r
+      // get the ID\r
+      _CrtMemBlockHeader *pHead;\r
+      // get a pointer to memory block header\r
+      pHead = pHdr(pvData);\r
+      nSize = pHead->nDataSize;\r
+      lRequest = pHead->lRequest; // This is the ID!\r
+\r
+      if (pHead->nBlockUse == _IGNORE_BLOCK)\r
+      {\r
+        InterlockedDecrement(&g_lMallocCalled);\r
+        if (pfnOldCrtAllocHook != NULL)\r
+          pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);\r
+        return TRUE;\r
+      }\r
+    }\r
+  }\r
+\r
+  if (g_ulShowStackAtAlloc > 0) {\r
+    _ftprintf( g_fFile, _T("##### Memory operation: %s a %d-byte '%s' block (# %ld)"),\r
+      operation[nAllocType], nSize, blockType[_BLOCK_TYPE(nBlockUse)], lRequest );\r
+    if ( pvData != NULL )\r
+      _ftprintf( g_fFile, _T(" at 0x%X"), pvData );\r
+    _ftprintf(g_fFile, _T("\n"));\r
+  }\r
+\r
+  if (nAllocType == _HOOK_FREE) { // freeing:\r
+    if (lRequest != 0) {  // RequestID was found\r
+      BOOL bRet;\r
+      // Try to find the RequestID in the Hash-Table, mark it that it was freed\r
+      bRet = AllocHashRemove(lRequest);\r
+      if(g_ulShowStackAtAlloc > 0) {\r
+        if (bRet == FALSE) {\r
+          // RequestID not found!\r
+          _ftprintf(g_fFile, _T("###### RequestID not found in hash table for FREEING (%i)!\n"), lRequest);\r
+        }\r
+      } // g_ulShowStackAtAlloc > 0\r
+    }\r
+    else {\r
+      if(g_ulShowStackAtAlloc > 0) {\r
+      // No valid RequestID found, display error\r
+      _ftprintf(g_fFile, _T("###### No valid RequestID for FREEING! (0x%X)\n"), pvData);\r
\r
+      }\r
+    }\r
+  }  // freeing\r
+\r
+  if (nAllocType == _HOOK_REALLOC) { // re-allocating\r
+    // Try to get the header information\r
+    if (_CrtIsValidHeapPointer(pvData)) {  // it is a valid Heap-Pointer\r
+      BOOL bRet;\r
+      LONG lReallocRequest;\r
+      // get the ID\r
+      _CrtMemBlockHeader *pHead;\r
+      // get a pointer to memory block header\r
+      pHead = pHdr(pvData);\r
+      // Try to find the RequestID in the Hash-Table, mark it that it was freed\r
+      lReallocRequest = pHead->lRequest;\r
+      bRet = AllocHashRemove(lReallocRequest);\r
+      if (g_ulShowStackAtAlloc > 0) {\r
+        if (bRet == FALSE) {\r
+          // RequestID not found!\r
+          _ftprintf(g_fFile, _T("###### RequestID not found in hash table for RE-ALLOCATING (%i)!\n"), lReallocRequest);\r
+        }\r
+        else {\r
+          _ftprintf(g_fFile, _T("##### Implicit freeing because of re-allocation (# old: %ld, new: %ld)\n"), lReallocRequest, lRequest);\r
+        }\r
+      }  // g_ulShowStackAtAlloc > 0\r
+    }  // ValidHeapPointer\r
+  }  // re-allocating\r
+\r
+  if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == _HOOK_FREE) ) {\r
+    InterlockedDecrement(&g_lMallocCalled);\r
+    // call the previous alloc hook\r
+    if (pfnOldCrtAllocHook != NULL)\r
+      pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);\r
+    return TRUE;\r
+  }\r
+\r
+  HANDLE hThread;\r
+  if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),\r
+    GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) == 0) {\r
+      // Something was wrong...\r
+      _ftprintf(g_fFile, _T("###### Could not call 'DuplicateHandle' successfully\n"));\r
+      InterlockedDecrement(&g_lMallocCalled);\r
+      // call the previous alloc hook\r
+      if (pfnOldCrtAllocHook != NULL)\r
+        pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);\r
+      return TRUE;\r
+  }\r
+\r
+  CONTEXT c;\r
+  memset( &c, '\0', sizeof c );\r
+  c.ContextFlags = CONTEXT_FULL;\r
+\r
+  // Get the context of this thread\r
+#if 0\r
+  // init CONTEXT record so we know where to start the stackwalk\r
+  if ( MyGetCurrentThreadContext( hThread, &c )  == 0) {\r
+    if(g_ulShowStackAtAlloc > 1) {\r
+      _ftprintf(g_fFile, _T("###### Could not call 'GetThreadContext' successfully\n"));\r
+    }\r
+    InterlockedDecrement(&g_lMallocCalled);\r
+    // call the previous alloc hook\r
+    if (pfnOldCrtAllocHook != NULL)\r
+      pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);\r
+    CloseHandle(hThread);\r
+    return TRUE; // could not get context\r
+  }\r
+#else\r
+  __asm\r
+  {\r
+      call x\r
+ x:   pop eax\r
+      mov c.Eip, eax\r
+      mov c.Ebp, ebp\r
+  }\r
+#endif\r
+\r
+  if(g_ulShowStackAtAlloc > 1) {\r
+    if(g_ulShowStackAtAlloc > 2) {\r
+      // output the callstack\r
+      ShowStack( hThread, c, g_fFile);\r
+    }\r
+    else {\r
+      // Output only (re)allocs\r
+      if (nAllocType != _HOOK_FREE) {\r
+        ShowStack( hThread, c, g_fFile);\r
+      }\r
+    }\r
+  }  // g_ulShowStackAtAlloc > 1\r
+  CloseHandle( hThread );\r
+\r
+  // Only isert in the Hash-Table if it is not a "freeing"\r
+  if (nAllocType != _HOOK_FREE) {\r
+    if(lRequest != 0) // Always a valid RequestID should be provided (see comments in the header)\r
+      AllocHashInsert(lRequest, c, nSize);\r
+  }\r
+\r
+  InterlockedDecrement(&g_lMallocCalled);\r
+  // call the previous alloc hook\r
+  if (pfnOldCrtAllocHook != NULL)\r
+    pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);\r
+  return TRUE; // allow the memory operation to proceed\r
+}\r
+\r
+\r
+\r
+\r
+// ##########################################################################################\r
+// ##########################################################################################\r
+// ##########################################################################################\r
+// ##########################################################################################\r
+\r
+#define gle (GetLastError())\r
+#define lenof(a) (sizeof(a) / sizeof((a)[0]))\r
+#define MAXNAMELEN 1024 // max name length for found symbols\r
+#define IMGSYMLEN ( sizeof IMAGEHLP_SYMBOL64 )\r
+#define TTBUFLEN 8096 // for a temp buffer (2^13)\r
+\r
+\r
+\r
+// SymCleanup()\r
+typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );\r
+tSC pSC = NULL;\r
+\r
+// SymFunctionTableAccess64()\r
+typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase );\r
+tSFTA pSFTA = NULL;\r
+\r
+// SymGetLineFromAddr64()\r
+typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,\r
+  OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line );\r
+tSGLFA pSGLFA = NULL;\r
+\r
+// SymGetModuleBase64()\r
+typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr );\r
+tSGMB pSGMB = NULL;\r
+\r
+// SymGetModuleInfo64()\r
+typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PIMAGEHLP_MODULE64 ModuleInfo );\r
+tSGMI pSGMI = NULL;\r
+\r
+// SymGetOptions()\r
+typedef DWORD (__stdcall *tSGO)( VOID );\r
+tSGO pSGO = NULL;\r
+\r
+// SymGetSymFromAddr64()\r
+typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,\r
+  OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol );\r
+tSGSFA pSGSFA = NULL;\r
+\r
+// SymInitialize()\r
+typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );\r
+tSI pSI = NULL;\r
+\r
+// SymLoadModule64()\r
+typedef DWORD (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile,\r
+  IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );\r
+tSLM pSLM = NULL;\r
+\r
+// SymSetOptions()\r
+typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );\r
+tSSO pSSO = NULL;\r
+\r
+// StackWalk64()\r
+typedef BOOL (__stdcall *tSW)( \r
+  DWORD MachineType, \r
+  HANDLE hProcess,\r
+  HANDLE hThread, \r
+  LPSTACKFRAME64 StackFrame, \r
+  PVOID ContextRecord,\r
+  PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,\r
+  PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,\r
+  PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,\r
+  PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );\r
+tSW pSW = NULL;\r
+\r
+// UnDecorateSymbolName()\r
+typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName,\r
+  DWORD UndecoratedLength, DWORD Flags );\r
+tUDSN pUDSN = NULL;\r
+\r
+\r
+\r
+struct ModuleEntry\r
+{\r
+  std::string imageName;\r
+  std::string moduleName;\r
+  DWORD baseAddress;\r
+  DWORD size;\r
+};\r
+typedef std::vector< ModuleEntry > ModuleList;\r
+typedef ModuleList::iterator ModuleListIter;\r
+\r
+// **************************************** ToolHelp32 ************************\r
+#define MAX_MODULE_NAME32 255\r
+#define TH32CS_SNAPMODULE   0x00000008\r
+#pragma pack( push, 8 )\r
+typedef struct tagMODULEENTRY32\r
+{\r
+    DWORD   dwSize;\r
+    DWORD   th32ModuleID;       // This module\r
+    DWORD   th32ProcessID;      // owning process\r
+    DWORD   GlblcntUsage;       // Global usage count on the module\r
+    DWORD   ProccntUsage;       // Module usage count in th32ProcessID's context\r
+    BYTE  * modBaseAddr;        // Base address of module in th32ProcessID's context\r
+    DWORD   modBaseSize;        // Size in bytes of module starting at modBaseAddr\r
+    HMODULE hModule;            // The hModule of this module in th32ProcessID's context\r
+    char    szModule[MAX_MODULE_NAME32 + 1];\r
+    char    szExePath[MAX_PATH];\r
+} MODULEENTRY32;\r
+typedef MODULEENTRY32 *  PMODULEENTRY32;\r
+typedef MODULEENTRY32 *  LPMODULEENTRY32;\r
+#pragma pack( pop )\r
+\r
+\r
+\r
+static bool GetModuleListTH32(ModuleList& modules, DWORD pid, FILE *fLogFile)\r
+{\r
+  // CreateToolhelp32Snapshot()\r
+  typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID);\r
+  // Module32First()\r
+  typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);\r
+  // Module32Next()\r
+  typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);\r
+\r
+  // try both dlls...\r
+  const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") };\r
+  HINSTANCE hToolhelp;\r
+  tCT32S pCT32S;\r
+  tM32F pM32F;\r
+  tM32N pM32N;\r
+\r
+  HANDLE hSnap;\r
+  MODULEENTRY32 me;\r
+  me.dwSize = sizeof(me);\r
+  bool keepGoing;\r
+  ModuleEntry e;\r
+  int i;\r
+\r
+  for (i = 0; i<lenof(dllname); i++ )\r
+  {\r
+    hToolhelp = LoadLibrary( dllname[i] );\r
+    if (hToolhelp == NULL)\r
+      continue;\r
+    pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");\r
+    pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First");\r
+    pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next");\r
+    if ( pCT32S != 0 && pM32F != 0 && pM32N != 0 )\r
+      break; // found the functions!\r
+    FreeLibrary(hToolhelp);\r
+    hToolhelp = NULL;\r
+  }\r
+\r
+  if (hToolhelp == NULL)\r
+    return false;\r
+\r
+  hSnap = pCT32S( TH32CS_SNAPMODULE, pid );\r
+  if (hSnap == (HANDLE) -1)\r
+    return false;\r
+\r
+  keepGoing = !!pM32F( hSnap, &me );\r
+  while (keepGoing)\r
+  {\r
+    e.imageName = me.szExePath;\r
+    e.moduleName = me.szModule;\r
+    e.baseAddress = (DWORD) me.modBaseAddr;\r
+    e.size = me.modBaseSize;\r
+    modules.push_back( e );\r
+    keepGoing = !!pM32N( hSnap, &me );\r
+  }\r
+\r
+  CloseHandle(hSnap);\r
+  FreeLibrary(hToolhelp);\r
+\r
+  return modules.size() != 0;\r
+}  // GetModuleListTH32\r
+\r
+\r
+// **************************************** PSAPI ************************\r
+typedef struct _MODULEINFO {\r
+    LPVOID lpBaseOfDll;\r
+    DWORD SizeOfImage;\r
+    LPVOID EntryPoint;\r
+} MODULEINFO, *LPMODULEINFO;\r
+\r
+static bool GetModuleListPSAPI(ModuleList &modules, DWORD pid, HANDLE hProcess, FILE *fLogFile)\r
+{\r
+  // EnumProcessModules()\r
+  typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded );\r
+  // GetModuleFileNameEx()\r
+  typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );\r
+  // GetModuleBaseName()\r
+  typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );\r
+  // GetModuleInformation()\r
+  typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize );\r
+\r
+  HINSTANCE hPsapi;\r
+  tEPM pEPM;\r
+  tGMFNE pGMFNE;\r
+  tGMBN pGMBN;\r
+  tGMI pGMI;\r
+\r
+  DWORD i;\r
+  ModuleEntry e;\r
+  DWORD cbNeeded;\r
+  MODULEINFO mi;\r
+  HMODULE *hMods = 0;\r
+  char *tt = 0;\r
+\r
+  hPsapi = LoadLibrary( _T("psapi.dll") );\r
+  if ( hPsapi == 0 )\r
+    return false;\r
+\r
+  modules.clear();\r
+\r
+  pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" );\r
+  pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" );\r
+  pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" );\r
+  pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );\r
+  if ( pEPM == 0 || pGMFNE == 0 || pGMBN == 0 || pGMI == 0 )\r
+  {\r
+    // we couldn´t find all functions\r
+    FreeLibrary( hPsapi );\r
+    return false;\r
+  }\r
+\r
+  hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));\r
+  tt = (char*) malloc(sizeof(char) * TTBUFLEN);\r
+\r
+  if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) )\r
+  {\r
+    _ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );\r
+    goto cleanup;\r
+  }\r
+\r
+  if ( cbNeeded > TTBUFLEN )\r
+  {\r
+    _ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );\r
+    goto cleanup;\r
+  }\r
+\r
+  for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ )\r
+  {\r
+    // base address, size\r
+    pGMI(hProcess, hMods[i], &mi, sizeof mi );\r
+    e.baseAddress = (DWORD) mi.lpBaseOfDll;\r
+    e.size = mi.SizeOfImage;\r
+    // image file name\r
+    tt[0] = 0;\r
+    pGMFNE(hProcess, hMods[i], tt, TTBUFLEN );\r
+    e.imageName = tt;\r
+    // module name\r
+    tt[0] = 0;\r
+    pGMBN(hProcess, hMods[i], tt, TTBUFLEN );\r
+    e.moduleName = tt;\r
+\r
+    modules.push_back(e);\r
+  }\r
+\r
+cleanup:\r
+  if (hPsapi)\r
+    FreeLibrary(hPsapi);\r
+  free(tt);\r
+  free(hMods);\r
+\r
+  return modules.size() != 0;\r
+}  // GetModuleListPSAPI\r
+\r
+\r
+static bool GetModuleList(ModuleList& modules, DWORD pid, HANDLE hProcess, FILE *fLogFile)\r
+{\r
+  // first try toolhelp32\r
+  if (GetModuleListTH32(modules, pid, fLogFile) )\r
+    return true;\r
+  // then try psapi\r
+  return GetModuleListPSAPI(modules, pid, hProcess, fLogFile);\r
+}  // GetModuleList\r
+\r
+\r
+static void EnumAndLoadModuleSymbols( HANDLE hProcess, DWORD pid, FILE *fLogFile )\r
+{\r
+  static ModuleList modules;\r
+  static ModuleListIter it;\r
+  char *img, *mod;\r
+\r
+  // fill in module list\r
+  GetModuleList(modules, pid, hProcess, fLogFile);\r
+\r
+  for ( it = modules.begin(); it != modules.end(); ++ it )\r
+  {\r
+    // SymLoadModule() wants writeable strings\r
+    img = strdup(it->imageName.c_str());\r
+    mod = strdup(it->moduleName.c_str());\r
+\r
+    pSLM( hProcess, 0, img, mod, it->baseAddress, it->size );\r
+\r
+    free(img);\r
+    free(mod);\r
+    std::string s;\r
+  }\r
+}  // EnumAndLoadModuleSymbols\r
+\r
+static int InitStackWalk(void)\r
+{\r
+  if (g_bInitialized != FALSE)\r
+    return 0;  // already initialized\r
+\r
+  // 02-12-19: Now we only support dbghelp.dll!\r
+  //           To use it on NT you have to install the redistrubutable for DBGHELP.DLL\r
+  g_hImagehlpDll = LoadLibrary( _T("dbghelp.dll") );\r
+  if ( g_hImagehlpDll == NULL )\r
+  {\r
+    printf( "LoadLibrary( \"dbghelp.dll\" ): GetLastError = %lu\n", gle );\r
+    g_bInitialized = FALSE;\r
+    return 1;\r
+  }\r
+\r
+  // now we only support the newer dbghlp.dll with the "64"-functions (StackWalk64, a.s.o.)\r
+  // If your dbghlp.dll does not support this, please download the redistributable from MS\r
+  // Normally from: http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=CD1FC4B2-0885-47F4-AF45-7FD5E14DB6C0\r
+\r
+  pSC = (tSC) GetProcAddress( g_hImagehlpDll, "SymCleanup" );\r
+  pSFTA = (tSFTA) GetProcAddress( g_hImagehlpDll, "SymFunctionTableAccess64" );\r
+  pSGLFA = (tSGLFA) GetProcAddress( g_hImagehlpDll, "SymGetLineFromAddr64" );\r
+  pSGMB = (tSGMB) GetProcAddress( g_hImagehlpDll, "SymGetModuleBase64" );\r
+  pSGMI = (tSGMI) GetProcAddress( g_hImagehlpDll, "SymGetModuleInfo64" );\r
+  pSGO = (tSGO) GetProcAddress( g_hImagehlpDll, "SymGetOptions" );\r
+  pSGSFA = (tSGSFA) GetProcAddress( g_hImagehlpDll, "SymGetSymFromAddr64" );\r
+  pSI = (tSI) GetProcAddress( g_hImagehlpDll, "SymInitialize" );\r
+  pSSO = (tSSO) GetProcAddress( g_hImagehlpDll, "SymSetOptions" );\r
+  pSW = (tSW) GetProcAddress( g_hImagehlpDll, "StackWalk64" );\r
+  pUDSN = (tUDSN) GetProcAddress( g_hImagehlpDll, "UnDecorateSymbolName" );\r
+  pSLM = (tSLM) GetProcAddress( g_hImagehlpDll, "SymLoadModule64" );\r
+\r
+  if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL ||\r
+    pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL ||\r
+    pSW == NULL || pUDSN == NULL || pSLM == NULL )\r
+  {\r
+    printf( "GetProcAddress(): some required function not found.\n" );\r
+    FreeLibrary( g_hImagehlpDll );\r
+    g_bInitialized = FALSE;\r
+    return 1;\r
+  }\r
+\r
+  g_bInitialized = TRUE;\r
+  InitializeCriticalSection(&g_csFileOpenClose);\r
+  return 0;\r
+}\r
+\r
+// This function if NOT multi-threading capable\r
+// It should only be called from the main-Function!\r
+int InitAllocCheckWN(eAllocCheckOutput eOutput, LPCTSTR pszFileName, ULONG ulShowStackAtAlloc) {\r
+  if (g_bInitialized) {\r
+    return 2;  // already initialized!\r
+  }\r
+  if (ulShowStackAtAlloc <= 3)\r
+    g_ulShowStackAtAlloc = ulShowStackAtAlloc;\r
+  else\r
+    g_ulShowStackAtAlloc = 0;\r
+\r
+  if (pszFileName != NULL) \r
+    g_pszAllocLogName = _tcsdup(pszFileName);\r
+  else\r
+    g_pszAllocLogName = NULL;\r
+\r
+  g_CallstackOutputType = eOutput;\r
+\r
+#ifdef _DEBUG\r
+  AllocHashInit();\r
+\r
+#ifdef WITH_IMALLOC_SPY\r
+  HRESULT hr;\r
+  // erzeuge mein malloc-Spy object\r
+  LPMALLOCSPY pMallocSpy = new CMallocSpy(); // wird später durch Release freigegeben\r
+  if (pMallocSpy != NULL)\r
+  {\r
+    // CoInitilize(); // ??? Ist dies notwendig ?\r
+    hr = CoRegisterMallocSpy(pMallocSpy);\r
+    if FAILED(hr)\r
+    {\r
+      _tprintf(_T("\nCoRegisterMallocSpay failed with %.8x"), hr);\r
+    }\r
+  }\r
+#endif\r
+\r
+  // save the previous alloc hook\r
+  pfnOldCrtAllocHook = _CrtSetAllocHook(MyAllocHook);\r
+#endif\r
+\r
+  return InitStackWalk();\r
+}  // InitAllocCheckWN\r
+\r
+static TCHAR s_szExceptionLogFileName[_MAX_PATH] = _T("\\exceptions.log");  // default\r
+static BOOL s_bUnhandledExeptionFilterSet = FALSE;\r
+static LONG __stdcall CrashHandlerExceptionFilter(EXCEPTION_POINTERS* pExPtrs)\r
+{\r
+  if (pExPtrs->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW)\r
+  {\r
+    static char MyStack[1024*128];  // be sure that we have enought space...\r
+    // it assumes that DS and SS are the same!!! (this is the case for Win32)\r
+    // change the stack only if the selectors are the same (this is the case for Win32)\r
+    //__asm push offset MyStack[1024*128];\r
+    //__asm pop esp;\r
+  __asm mov eax,offset MyStack[1024*128];\r
+  __asm mov esp,eax;\r
+  }\r
+\r
+   LONG lRet;\r
+   lRet = StackwalkFilter(pExPtrs, /*EXCEPTION_CONTINUE_SEARCH*/EXCEPTION_EXECUTE_HANDLER, s_szExceptionLogFileName);\r
+   TCHAR lString[500];\r
+   _stprintf(lString,\r
+      _T("*** Unhandled Exception!\n")\r
+      _T("   ExpCode: 0x%8.8X\n")\r
+      _T("   ExpFlags: %d\n")\r
+      _T("   ExpAddress: 0x%8.8X\n")\r
+      _T("   Please report!"),\r
+      pExPtrs->ExceptionRecord->ExceptionCode,\r
+      pExPtrs->ExceptionRecord->ExceptionFlags,\r
+      pExPtrs->ExceptionRecord->ExceptionAddress);\r
+   FatalAppExit(-1,lString);\r
+   return lRet;\r
+}\r
+\r
+int InitAllocCheck(eAllocCheckOutput eOutput, BOOL bSetUnhandledExeptionFilter, ULONG ulShowStackAtAlloc)  // will create the filename by itself\r
+{\r
+  TCHAR szModName[_MAX_PATH];\r
+  if (GetModuleFileName(NULL, szModName, sizeof(szModName)/sizeof(TCHAR)) != 0)\r
+  {\r
+    _tcscpy(s_szExceptionLogFileName, szModName);\r
+    if (eOutput == ACOutput_XML)\r
+      _tcscat(s_szExceptionLogFileName, _T(".exp.xml"));\r
+    else\r
+      _tcscat(s_szExceptionLogFileName, _T(".exp.log"));\r
+\r
+    if (eOutput == ACOutput_XML)\r
+      _tcscat(szModName, _T(".mem.xml-leaks"));\r
+    else\r
+      _tcscat(szModName, _T(".mem.log"));\r
+  }\r
+  else\r
+  {\r
+    if (eOutput == ACOutput_XML)\r
+      _tcscpy(szModName, _T("\\mem-leaks.xml-leaks"));  // default\r
+    else\r
+      _tcscpy(szModName, _T("\\mem-leaks.log"));  // default\r
+  }\r
+\r
+  if ((bSetUnhandledExeptionFilter != FALSE) && (s_bUnhandledExeptionFilterSet == FALSE) )\r
+  {\r
+    // set global exception handler (for handling all unhandled exceptions)\r
+    SetUnhandledExceptionFilter(CrashHandlerExceptionFilter);\r
+    s_bUnhandledExeptionFilterSet = TRUE;\r
+  }\r
+\r
+  return InitAllocCheckWN(eOutput, szModName, ulShowStackAtAlloc);\r
+}\r
+\r
+\r
+// This function if NOT multi-threading capable\r
+// It should only be called from the main-Function!\r
+//   Returns the number of bytes that are not freed (leaks)\r
+ULONG DeInitAllocCheck(void) {\r
+  ULONG ulRet = 0;\r
+  if (g_bInitialized) {\r
+\r
+#ifdef _DEBUG\r
+    InterlockedIncrement(&g_lMallocCalled); // No deactivate MyAllocHook, because StackWalker will allocate some memory)\r
+    ulRet = AllocHashDeinit();  // output the not freed memory\r
+    // remove the hook and set the old one\r
+    _CrtSetAllocHook(pfnOldCrtAllocHook);\r
+\r
+#ifdef WITH_IMALLOC_SPY\r
+    CoRevokeMallocSpy();\r
+#endif\r
+\r
+#endif\r
+\r
+    EnterCriticalSection(&g_csFileOpenClose);  // wait until a running stack dump was created\r
+    g_bInitialized = FALSE;\r
+\r
+    // de-init symbol handler etc. (SymCleanup())\r
+    if (pSC != NULL)\r
+      pSC( GetCurrentProcess() );\r
+    FreeLibrary( g_hImagehlpDll );\r
+\r
+    LeaveCriticalSection(&g_csFileOpenClose);\r
+    if (g_pszAllocLogName != NULL) {\r
+      free(g_pszAllocLogName);\r
+      g_pszAllocLogName = NULL;\r
+    }\r
+    if (g_fFile != NULL) {\r
+       fclose(g_fFile);\r
+       g_fFile = NULL;\r
+    }\r
+\r
+    DeleteCriticalSection(&g_csFileOpenClose);\r
+    InterlockedDecrement(&g_lMallocCalled);\r
+  }\r
+\r
+  if (s_bUnhandledExeptionFilterSet != TRUE)\r
+  {\r
+    SetUnhandledExceptionFilter(NULL);\r
+    s_bUnhandledExeptionFilterSet = FALSE;\r
+  }\r
+  return ulRet;\r
+}  // DeInitAllocCheck\r
+\r
+\r
+\r
+void OnlyInstallUnhandeldExceptionFilter(eAllocCheckOutput eOutput)\r
+{\r
+  if (s_bUnhandledExeptionFilterSet == FALSE)\r
+  {\r
+    TCHAR szModName[_MAX_PATH];\r
+    if (GetModuleFileName(NULL, szModName, sizeof(szModName)/sizeof(TCHAR)) != 0)\r
+    {\r
+      _tcscpy(s_szExceptionLogFileName, szModName);\r
+      if (eOutput == ACOutput_XML)\r
+        _tcscat(s_szExceptionLogFileName, _T(".exp.xml"));\r
+      else\r
+        _tcscat(s_szExceptionLogFileName, _T(".exp.log"));\r
+\r
+      if (eOutput == ACOutput_XML)\r
+        _tcscat(szModName, _T(".mem.xml-leaks"));\r
+      else\r
+        _tcscat(szModName, _T(".mem.log"));\r
+    }\r
+    else\r
+    {\r
+      if (eOutput == ACOutput_XML)\r
+        _tcscpy(szModName, _T("\\mem-leaks.xml-leaks"));  // default\r
+      else\r
+        _tcscpy(szModName, _T("\\mem-leaks.log"));  // default\r
+    }\r
+    // set it again; WARNING: this will override the setting for a possible AllocCheck-Setting\r
+    g_CallstackOutputType = eOutput;\r
+\r
+    // set global exception handler (for handling all unhandled exceptions)\r
+    SetUnhandledExceptionFilter(CrashHandlerExceptionFilter);\r
+    s_bUnhandledExeptionFilterSet = TRUE;\r
+  }\r
+}\r
+\r
+\r
+\r
+static TCHAR *GetExpectionCodeText(DWORD dwExceptionCode) {\r
+  switch(dwExceptionCode) {\r
+  case EXCEPTION_ACCESS_VIOLATION: return _T("ACCESS VIOLATION");\r
+  case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return _T("ARRAY BOUNDS EXCEEDED");\r
+  case EXCEPTION_BREAKPOINT: return _T("BREAKPOINT");\r
+  case EXCEPTION_DATATYPE_MISALIGNMENT: return _T("DATATYPE MISALIGNMENT");\r
+  case EXCEPTION_FLT_DENORMAL_OPERAND: return _T("FLT DENORMAL OPERAND");\r
+  case EXCEPTION_FLT_DIVIDE_BY_ZERO: return _T("FLT DIVIDE BY ZERO");\r
+  case EXCEPTION_FLT_INEXACT_RESULT: return _T("FLT INEXACT RESULT");\r
+  case EXCEPTION_FLT_INVALID_OPERATION: return _T("FLT INVALID OPERATION");\r
+  case EXCEPTION_FLT_OVERFLOW: return _T("FLT OVERFLOW");\r
+  case EXCEPTION_FLT_STACK_CHECK: return _T("FLT STACK CHECK");\r
+  case EXCEPTION_FLT_UNDERFLOW: return _T("FLT UNDERFLOW");\r
+  case EXCEPTION_ILLEGAL_INSTRUCTION: return _T("ILLEGAL INSTRUCTION");\r
+  case EXCEPTION_IN_PAGE_ERROR: return _T("IN PAGE ERROR");\r
+  case EXCEPTION_INT_DIVIDE_BY_ZERO: return _T("INT DIVIDE BY ZERO");\r
+  case EXCEPTION_INT_OVERFLOW: return _T("INT OVERFLOW");\r
+  case EXCEPTION_INVALID_DISPOSITION: return _T("INVALID DISPOSITION");\r
+  case EXCEPTION_NONCONTINUABLE_EXCEPTION: return _T("NONCONTINUABLE EXCEPTION");\r
+  case EXCEPTION_PRIV_INSTRUCTION: return _T("PRIV INSTRUCTION");\r
+  case EXCEPTION_SINGLE_STEP: return _T("SINGLE STEP");\r
+  case EXCEPTION_STACK_OVERFLOW: return _T("STACK OVERFLOW");\r
+  case DBG_CONTROL_C : return _T("DBG CONTROL C ");\r
+  default:\r
+    return _T("<unkown exception>");\r
+  }\r
+}  // GetExpectionCodeText\r
+\r
+// Function is not multi-threading safe, because of static char!\r
+static TCHAR *GetAdditionalExpectionCodeText(PEXCEPTION_RECORD pExceptionRecord) {\r
+  static TCHAR szTemp[100];\r
+\r
+  switch(pExceptionRecord->ExceptionCode) {\r
+  case EXCEPTION_ACCESS_VIOLATION:\r
+    if (pExceptionRecord->NumberParameters == 2) {\r
+      switch(pExceptionRecord->ExceptionInformation[0]) {\r
+      case 0: // read attempt\r
+        _stprintf(szTemp, _T(" read attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);\r
+        return szTemp;\r
+      case 1: // write attempt\r
+        _stprintf(szTemp, _T(" write attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);\r
+        return szTemp;\r
+      default:\r
+        return _T("");\r
+      }\r
+    }  // if (pExceptionRecord->NumberParameters == 2)\r
+    return _T("");\r
+  default:\r
+    return _T("");\r
+  }  // switch(pExceptionRecord->ExceptionCode)\r
+}  // GetAdditionalExpectionCodeText\r
+\r
+std::string SimpleXMLEncode(PCSTR szText)\r
+{\r
+  std::string szRet;\r
+\r
+  for (size_t i=0; i<strlen(szText); i++)\r
+  {\r
+    switch(szText[i])\r
+    {\r
+    case '&':\r
+      szRet.append("&amp;");\r
+      break;\r
+    case '<':\r
+      szRet.append("&lt;");\r
+      break;\r
+    case '>':\r
+      szRet.append("&gt;");\r
+      break;\r
+    case '"':\r
+      szRet.append("&quot;");\r
+      break;\r
+    case '\'':\r
+      szRet.append("&apos;");\r
+      break;\r
+    default:\r
+      szRet += szText[i];\r
+    }\r
+  }\r
+  return szRet;\r
+}\r
+\r
+\r
+// #################################################################################\r
+// #################################################################################\r
+// Here the Stackwalk-Part begins.\r
+//   Some of the code is from an example from a book \r
+//   But I couldn´t find the reference anymore... sorry...\r
+//   If someone knowns, please let me know...\r
+// #################################################################################\r
+// #################################################################################\r
+\r
+\r
+// if you use C++ exception handling: install a translator function\r
+// with set_se_translator(). In the context of that function (but *not*\r
+// afterwards), you can either do your stack dump, or save the CONTEXT\r
+// record as a local copy. Note that you must do the stack sump at the\r
+// earliest opportunity, to avoid the interesting stackframes being gone\r
+// by the time you do the dump.\r
+\r
+// status: \r
+// - EXCEPTION_CONTINUE_SEARCH: exception wird weitergereicht\r
+// - EXCEPTION_CONTINUE_EXECUTION: \r
+// - EXCEPTION_EXECUTE_HANDLER:\r
+DWORD StackwalkFilter( EXCEPTION_POINTERS *ep, DWORD status, LPCTSTR pszLogFile)\r
+{\r
+  HANDLE hThread;\r
+  FILE *fFile = stdout;  // default to stdout\r
+\r
+  if (pszLogFile != NULL) {  // a filename is provided\r
+    // Open the logfile\r
+    fFile = _tfopen(pszLogFile, _T("a"));\r
+    if (fFile != NULL) {  // Is the file too big?\r
+      long size;\r
+      fseek(fFile, 0, SEEK_END);\r
+      size = ftell(fFile);  // Get the size of the file\r
+      if (size >= LOG_FILE_MAX_SIZE) {\r
+        TCHAR *pszTemp = (TCHAR*) malloc(MAX_PATH);\r
+        // It is too big...\r
+        fclose(fFile);\r
+        _tcscpy(pszTemp, pszLogFile);\r
+        _tcscat(pszTemp, _T(".old"));\r
+        _tremove(pszTemp);  // Remove an old file, if exists\r
+        _trename(pszLogFile, pszTemp);  // rename the actual file\r
+        fFile = _tfopen(pszLogFile, _T("w"));  // create a new file\r
+        free(pszTemp);\r
+      }\r
+    }\r
+  }  // if (pszLogFile != NULL) \r
+  if (fFile == NULL) {\r
+    fFile = stdout;\r
+  }\r
+\r
+  // Write infos about the exception\r
+  if (g_CallstackOutputType == ACOutput_XML)\r
+  {\r
+    _ftprintf(fFile, _T("<EXCEPTION code=\"%8.8X\" addr=\"%8.8X\" "), \r
+      ep->ExceptionRecord->ExceptionCode,\r
+      ep->ExceptionRecord->ExceptionAddress);\r
+    WriteDateTime(fFile, TRUE);\r
+    _ftprintf(fFile, _T("code_desc=\"%s\" more_desc=\"%s\">\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),\r
+      GetAdditionalExpectionCodeText(ep->ExceptionRecord));\r
+  }\r
+  else\r
+  {\r
+    _ftprintf(fFile, _T("######## EXCEPTION: 0x%8.8X at address: 0x%8.8X"), \r
+      ep->ExceptionRecord->ExceptionCode,\r
+      ep->ExceptionRecord->ExceptionAddress);\r
+    _ftprintf(fFile, _T(": %s %s\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),\r
+      GetAdditionalExpectionCodeText(ep->ExceptionRecord));\r
+  }\r
+\r
+  DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),\r
+    GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS );\r
+  ShowStack( hThread, *(ep->ContextRecord), fFile);\r
+  CloseHandle( hThread );\r
+\r
+  if (g_CallstackOutputType == ACOutput_XML)\r
+    _ftprintf(fFile, _T("</EXCEPTION>\n"));\r
+\r
+  fclose(fFile);\r
+\r
+  return status;\r
+}  // StackwalkFilter\r
+\r
+void ShowStack( HANDLE hThread, CONTEXT& c, LPCTSTR pszLogFile)\r
+{\r
+  FILE *fFile = stdout;  // default to stdout\r
+\r
+  if (pszLogFile != NULL) {  // a filename is available\r
+    // Open the logfile\r
+    fFile = _tfopen(pszLogFile, _T("a"));\r
+    if (fFile != NULL) {  // Is the file too big?\r
+      long size;\r
+      fseek(fFile, 0, SEEK_END);\r
+      size = ftell(fFile);  // Get the size of the file\r
+      if (size >= LOG_FILE_MAX_SIZE) {\r
+        TCHAR *pszTemp = (TCHAR*) malloc(MAX_PATH);\r
+        // It is too big...\r
+        fclose(fFile);\r
+        _tcscpy(pszTemp, pszLogFile);\r
+        _tcscat(pszTemp, _T(".old"));\r
+        _tremove(pszTemp);  // Remove an old file, if exists\r
+        _trename(pszLogFile, pszTemp);  // rename the actual file\r
+        fFile = _tfopen(pszLogFile, _T("w"));  // open new file\r
+        free(pszTemp);\r
+      }\r
+    }\r
+  }  // if (pszLogFile != NULL) \r
+  if (fFile == NULL) {\r
+    fFile = stdout;\r
+  }\r
+\r
+  ShowStack( hThread, c, fFile);\r
+\r
+  fclose(fFile);\r
+}\r
+\r
+\r
+static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile) {\r
+  ShowStackRM(hThread, c, fLogFile, NULL, GetCurrentProcess());\r
+}\r
+\r
+static void ShowStackRM( HANDLE hThread, CONTEXT& c, FILE *fLogFile, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryFunction, HANDLE hSWProcess) {\r
+  // normally, call ImageNtHeader() and use machine info from PE header\r
+  // but we assume that it is an I386 image...\r
+  DWORD imageType = IMAGE_FILE_MACHINE_I386;\r
+  HANDLE hProcess = GetCurrentProcess(); // hProcess normally comes from outside but we only do the stackdump in our own process\r
+  int frameNum; // counts walked frames\r
+  DWORD64 offsetFromSymbol; // tells us how far from the symbol we were\r
+  DWORD offsetFromLine; // tells us how far from the line we were\r
+  DWORD symOptions; // symbol handler settings\r
+\r
+  static IMAGEHLP_SYMBOL64 *pSym = NULL;\r
+  char undName[MAXNAMELEN]; // undecorated name\r
+  char undFullName[MAXNAMELEN]; // undecorated name with all shenanigans\r
+  IMAGEHLP_MODULE64 Module;\r
+  IMAGEHLP_LINE64 Line;\r
+  BOOL bXMLTagWrote;\r
+\r
+  std::string symSearchPath;\r
+\r
+  static bool bFirstTime = TRUE;\r
+\r
+  // If no logfile is present, outpur to "stdout"\r
+  if (fLogFile == NULL) {\r
+    fLogFile = stdout;\r
+  }\r
+\r
+  STACKFRAME64 s; // in/out stackframe\r
+  memset( &s, '\0', sizeof s );\r
+\r
+  if ( (g_bInitialized == FALSE) && (bFirstTime == TRUE) ) {\r
+    InitStackWalk();\r
+  }\r
+\r
+  if (g_bInitialized == FALSE)\r
+  {\r
+    // Could not init!!!!\r
+    bFirstTime = FALSE;\r
+    _ftprintf(fLogFile, _T("%lu: Stackwalker not initialized (or was not able to initialize)!\n"), g_dwShowCount);\r
+    return;\r
+  }\r
+\r
+// Critical section begin...\r
+  EnterCriticalSection(&g_csFileOpenClose);\r
+\r
+  InterlockedIncrement((long*) &g_dwShowCount);  // erhöhe counter\r
+\r
+\r
+  // NOTE: normally, the exe directory and the current directory should be taken\r
+  // from the target process. The current dir would be gotten through injection\r
+  // of a remote thread; the exe fir through either ToolHelp32 or PSAPI.\r
+\r
+  if (pSym == NULL) {\r
+    pSym = (IMAGEHLP_SYMBOL64 *) malloc( IMGSYMLEN + MAXNAMELEN );\r
+    if (!pSym) goto cleanup;  // not enough memory...\r
+  }\r
+\r
+  if (g_CallstackOutputType != ACOutput_XML)\r
+  {\r
+    _ftprintf(fLogFile, _T("%lu: "), g_dwShowCount);\r
+    WriteDateTime(fLogFile);\r
+    _ftprintf(fLogFile, _T("\n"));\r
+  }\r
+\r
+\r
+  if (bFirstTime) {\r
+\r
+    CHAR *tt, *p;\r
+\r
+    tt = (CHAR*) malloc(sizeof(CHAR) * TTBUFLEN); // Get the temporary buffer\r
+    if (!tt) goto cleanup;  // not enough memory...\r
+\r
+    // build symbol search path from:\r
+    symSearchPath = "";\r
+    // current directory\r
+    if ( GetCurrentDirectoryA( TTBUFLEN, tt ) )\r
+      symSearchPath += tt + std::string( ";" );\r
+    // dir with executable\r
+    if ( GetModuleFileNameA( 0, tt, TTBUFLEN ) )\r
+    {\r
+      for ( p = tt + strlen( tt ) - 1; p >= tt; -- p )\r
+      {\r
+        // locate the rightmost path separator\r
+        if ( *p == '\\' || *p == '/' || *p == ':' )\r
+          break;\r
+      }\r
+      // if we found one, p is pointing at it; if not, tt only contains\r
+      // an exe name (no path), and p points before its first byte\r
+      if ( p != tt ) // path sep found?\r
+      {\r
+        if ( *p == ':' ) // we leave colons in place\r
+          ++ p;\r
+        *p = '\0'; // eliminate the exe name and last path sep\r
+        symSearchPath += tt + std::string( ";" );\r
+      }\r
+    }\r
+    // environment variable _NT_SYMBOL_PATH\r
+    if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", tt, TTBUFLEN ) )\r
+      symSearchPath += tt + std::string( ";" );\r
+    // environment variable _NT_ALTERNATE_SYMBOL_PATH\r
+    if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", tt, TTBUFLEN ) )\r
+      symSearchPath += tt + std::string( ";" );\r
+    // environment variable SYSTEMROOT\r
+    if ( GetEnvironmentVariableA( "SYSTEMROOT", tt, TTBUFLEN ) )\r
+      symSearchPath += tt + std::string( ";" );\r
+\r
+\r
+\r
+    if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon\r
+      symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 );\r
+\r
+    // why oh why does SymInitialize() want a writeable string?\r
+    strncpy( tt, symSearchPath.c_str(), TTBUFLEN );\r
+    tt[TTBUFLEN - 1] = '\0'; // if strncpy() overruns, it doesn't add the null terminator\r
+\r
+    // init symbol handler stuff (SymInitialize())\r
+    if ( ! pSI( hProcess, tt, false ) )\r
+    {\r
+      if (g_CallstackOutputType != ACOutput_XML)\r
+        _ftprintf(fLogFile, _T("%lu: SymInitialize(): GetLastError = %lu\n"), g_dwShowCount, gle );\r
+      if (tt) free( tt );\r
+      goto cleanup;\r
+    }\r
+\r
+    // SymGetOptions()\r
+    symOptions = pSGO();\r
+    symOptions |= SYMOPT_LOAD_LINES;\r
+    symOptions &= ~SYMOPT_UNDNAME;\r
+    symOptions &= ~SYMOPT_DEFERRED_LOADS;\r
+    pSSO( symOptions ); // SymSetOptions()\r
+\r
+    // Enumerate modules and tell dbghlp.dll about them.\r
+    // On NT, this is not necessary, but it won't hurt.\r
+    EnumAndLoadModuleSymbols( hProcess, GetCurrentProcessId(), fLogFile );\r
+\r
+    if (tt) \r
+      free( tt );\r
+  }  // bFirstTime = TRUE\r
+  bFirstTime = FALSE;\r
+\r
+  // init STACKFRAME for first call\r
+  // Notes: AddrModeFlat is just an assumption. I hate VDM debugging.\r
+  // Notes: will have to be #ifdef-ed for Alphas; MIPSes are dead anyway,\r
+  // and good riddance.\r
+  s.AddrPC.Offset = c.Eip;\r
+  s.AddrPC.Mode = AddrModeFlat;\r
+  s.AddrFrame.Offset = c.Ebp;\r
+  s.AddrFrame.Mode = AddrModeFlat;\r
+  s.AddrStack.Offset = c.Ebp;\r
+  s.AddrStack.Mode = AddrModeFlat;\r
+\r
+  memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN );\r
+  pSym->SizeOfStruct = IMGSYMLEN;\r
+  pSym->MaxNameLength = MAXNAMELEN;\r
+\r
+  memset( &Line, '\0', sizeof Line );\r
+  Line.SizeOfStruct = sizeof Line;\r
+\r
+  memset( &Module, '\0', sizeof Module );\r
+  Module.SizeOfStruct = sizeof Module;\r
+\r
+  for ( frameNum = 0; ; ++ frameNum )\r
+  {\r
+    // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())\r
+    // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can\r
+    // assume that either you are done, or that the stack is so hosed that the next\r
+    // deeper frame could not be found.\r
+    // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!\r
+    if ( ! pSW( imageType, hSWProcess, hThread, &s, NULL, ReadMemoryFunction, pSFTA, pSGMB, NULL ) )\r
+      break;\r
+\r
+    bXMLTagWrote = FALSE;\r
+\r
+    if (g_CallstackOutputType == ACOutput_Advanced)\r
+      _ftprintf(fLogFile, _T("\n%lu: %3d"), g_dwShowCount, frameNum);\r
+    if ( s.AddrPC.Offset == 0 )\r
+    {\r
+      // Special case: If we are here, we have no valid callstack entry!\r
+      switch(g_CallstackOutputType)\r
+      {\r
+      case ACOutput_Simple:\r
+        _ftprintf(fLogFile, _T("%lu: (-nosymbols- PC == 0)\n"), g_dwShowCount);\r
+        break;\r
+      case ACOutput_Advanced:\r
+        _ftprintf(fLogFile, _T("   (-nosymbols- PC == 0)\n"));\r
+        break;\r
+      case ACOutput_XML:\r
+        // TODO: ....\r
+        _ftprintf(fLogFile, _T("<STACKENTRY decl=\"(-nosymbols- PC == 0)\"/>\n"));\r
+        break;\r
+      }\r
+    }\r
+    else\r
+    {\r
+      // we seem to have a valid PC\r
+      undName[0] = 0;\r
+      undFullName[0] = 0;\r
+      offsetFromSymbol = 0;\r
+      // show procedure info (SymGetSymFromAddr())\r
+      if ( ! pSGSFA( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) )\r
+      {\r
+        if (g_CallstackOutputType == ACOutput_Advanced)\r
+        {\r
+          if ( gle != 487 )\r
+            _ftprintf(fLogFile, _T("   SymGetSymFromAddr(): GetLastError = %lu\n"), gle );\r
+          else\r
+            _ftprintf(fLogFile, _T("\n"));\r
+        }\r
+      }\r
+      else\r
+      {\r
+        // UnDecorateSymbolName()\r
+        pUDSN( pSym->Name, undName, MAXNAMELEN, UNDNAME_NAME_ONLY );\r
+        pUDSN( pSym->Name, undFullName, MAXNAMELEN, UNDNAME_COMPLETE );\r
+        if (g_CallstackOutputType == ACOutput_Advanced)\r
+        {\r
+          if (strlen(undName) > 0)\r
+            fprintf(fLogFile, "     %s %+ld bytes\n", undName, (long) offsetFromSymbol );\r
+          else\r
+          {\r
+            fprintf(fLogFile, "     Sig:  %s %+ld bytes\n", pSym->Name, (long) offsetFromSymbol );\r
+            strcpy(undName, pSym->Name);\r
+          }\r
+          fprintf(fLogFile, "%lu:     Decl: %s\n", g_dwShowCount, undFullName );\r
+        }\r
+      }\r
+      //if (g_CallstackOutputType == ACOutput_XML)\r
+      //  fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);\r
+\r
+      // show line number info, NT5.0-method (SymGetLineFromAddr())\r
+      offsetFromLine = 0;\r
+      if ( pSGLFA != NULL )\r
+      { // yes, we have SymGetLineFromAddr()\r
+        if ( ! pSGLFA( hProcess, s.AddrPC.Offset, &offsetFromLine, &Line ) )\r
+        {\r
+          if ( (gle != 487) && (frameNum > 0) )  // ignore error for first frame\r
+          {\r
+            if (g_CallstackOutputType == ACOutput_XML)\r
+            {\r
+              _ftprintf(fLogFile, _T("<STACKENTRY "));\r
+              bXMLTagWrote = TRUE;\r
+              fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);\r
+              _ftprintf(fLogFile, _T("srcfile=\"SymGetLineFromAddr(): GetLastError = %lu\" "), gle);\r
+            }\r
+            else\r
+              _ftprintf(fLogFile, _T("%lu: SymGetLineFromAddr(): GetLastError = %lu\n"), g_dwShowCount, gle );\r
+          }\r
+        }\r
+        else\r
+        {\r
+          switch(g_CallstackOutputType)\r
+          {\r
+          case ACOutput_Advanced:\r
+            fprintf(fLogFile, "%lu:     Line: %s(%lu) %+ld bytes\n", g_dwShowCount,\r
+              Line.FileName, Line.LineNumber, offsetFromLine );\r
+            break;\r
+          case ACOutput_Simple:\r
+            fprintf(fLogFile, "%lu: %s(%lu) %+ld bytes (%s)\n", g_dwShowCount,\r
+              Line.FileName, Line.LineNumber, offsetFromLine, undName);\r
+            break;\r
+          case ACOutput_XML:\r
+            _ftprintf(fLogFile, _T("<STACKENTRY "));\r
+            bXMLTagWrote = TRUE;\r
+            fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);\r
+            fprintf(fLogFile, "srcfile=\"%s\" line=\"%lu\" line_offset=\"%+ld\" ", \r
+              SimpleXMLEncode(Line.FileName).c_str(), Line.LineNumber, offsetFromLine, undName);\r
+            break;\r
+          }\r
+        }\r
+      } // yes, we have SymGetLineFromAddr()\r
+\r
+      // show module info (SymGetModuleInfo())\r
+      if ( (g_CallstackOutputType == ACOutput_Advanced) || (g_CallstackOutputType == ACOutput_XML) )\r
+      {\r
+        if ( ! pSGMI( hProcess, s.AddrPC.Offset, &Module ) )\r
+        {\r
+          if (g_CallstackOutputType == ACOutput_Advanced)\r
+            _ftprintf(fLogFile, _T("%lu: SymGetModuleInfo): GetLastError = %lu\n"), g_dwShowCount, gle );\r
+        }\r
+        else\r
+        { // got module info OK\r
+          char ty[80];\r
+          switch ( Module.SymType )\r
+          {\r
+          case SymNone:\r
+            strcpy( ty, "-nosymbols-" );\r
+            break;\r
+          case SymCoff:\r
+            strcpy( ty, "COFF" );\r
+            break;\r
+          case SymCv:\r
+            strcpy( ty, "CV" );\r
+            break;\r
+          case SymPdb:\r
+            strcpy( ty, "PDB" );\r
+            break;\r
+          case SymExport:\r
+            strcpy( ty, "-exported-" );\r
+            break;\r
+          case SymDeferred:\r
+            strcpy( ty, "-deferred-" );\r
+            break;\r
+          case SymSym:\r
+            strcpy( ty, "SYM" );\r
+            break;\r
+#if API_VERSION_NUMBER >= 9\r
+          case SymDia:\r
+            strcpy( ty, "DIA" );\r
+            break;\r
+#endif\r
+          default:\r
+            _snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType );\r
+            break;\r
+          }\r
+\r
+          if (g_CallstackOutputType == ACOutput_XML)\r
+          {\r
+            // now, check if the XML-Entry is written...\r
+            if (bXMLTagWrote == FALSE) \r
+            {\r
+              _ftprintf(fLogFile, _T("<STACKENTRY "));\r
+              bXMLTagWrote = TRUE;\r
+              fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);\r
+              _ftprintf(fLogFile, _T("srcfile=\"\" "));\r
+              bXMLTagWrote = TRUE;\r
+            }\r
+          }\r
+\r
+          if (g_CallstackOutputType == ACOutput_Advanced)\r
+          {\r
+            fprintf(fLogFile, "%lu:     Mod:  %s, base: %08lxh\n", g_dwShowCount,\r
+              Module.ModuleName, Module.BaseOfImage );\r
+            if (Module.SymType == SymNone) { // Gebe nur aus, wenn keine Symbole vorhanden sind!\r
+              _ftprintf(fLogFile, _T("%lu:     Offset: 0x%8.8x\n"), g_dwShowCount, s.AddrPC.Offset);\r
+              fprintf(fLogFile, "%lu:     Sym:  type: %s, file: %s\n", g_dwShowCount,\r
+                ty, Module.LoadedImageName );\r
+            }\r
+          }\r
+          else\r
+          {\r
+            // XML:\r
+            if (bXMLTagWrote == TRUE)\r
+              fprintf(fLogFile, "module=\"%s\" base=\"%08lx\" ", Module.ModuleName, Module.BaseOfImage);\r
+          }\r
+        } // got module info OK\r
+      }\r
+      if ( (g_CallstackOutputType == ACOutput_XML) && (bXMLTagWrote == TRUE) )\r
+        _ftprintf(fLogFile, _T("/>\n"));  // terminate the XML node\r
+\r
+    } // we seem to have a valid PC\r
+\r
+    // no return address means no deeper stackframe\r
+    if ( s.AddrReturn.Offset == 0 )\r
+    {\r
+      // avoid misunderstandings in the printf() following the loop\r
+      SetLastError( 0 );\r
+      break;\r
+    }\r
+\r
+  } // for ( frameNum )\r
+\r
+  if ( (g_CallstackOutputType != ACOutput_XML) && (gle != 0) )\r
+    _ftprintf(fLogFile, _T("\n%lu: StackWalk(): GetLastError = %lu\n"), g_dwShowCount, gle );\r
+\r
+cleanup:\r
+  //if (pSym) free( pSym );\r
+  if (fLogFile) {\r
+    _ftprintf(fLogFile, _T("\n\n"));\r
+    if (g_dwShowCount % 1000)\r
+      fflush(fLogFile);\r
+  }\r
+\r
+  LeaveCriticalSection(&g_csFileOpenClose);\r
+\r
+\r
+// Critical section end...\r
+}  // ShowStackRM\r
+\r
+#pragma warning(pop)\r