// ExceptionHandler.cpp Version 1.4 // // Copyright © 1998 Bruce Dawson // // This source file contains the exception handler for recording error // information after crashes. See ExceptionHandler.h for information // on how to hook it in. // // Author: Bruce Dawson // brucedawson@cygnus-software.com // // Modified by: Hans Dietrich // hdietrich2@hotmail.com // // Version 1.4: - Added invocation of XCrashReport.exe // // Version 1.3: - Added minidump output // // Version 1.1: - reformatted output for XP-like error report // - added ascii output to stack dump // // A paper by the original author can be found at: // http://www.cygnus-software.com/papers/release_debugging.html // /////////////////////////////////////////////////////////////////////////////// // Disable warnings generated by the Windows header files. #pragma warning(disable : 4514) #pragma warning(disable : 4201) #define _WIN32_WINDOWS 0x0500 // for IsDebuggerPresent #include "windows.h" #include #include "GetWinVer.h" #include "miniversion.h" #include "../nu/ns_wc.h" #include "minidump.h" #include ".\settings.h" #include "api__gen_crasher.h" extern char *winampVersion; extern Settings settings; #ifndef _countof #define _countof(array) (sizeof(array)/sizeof(array[0])) #endif const int NumCodeBytes = 16; // Number of code bytes to record. const int MaxStackDump = 3072; // Maximum number of DWORDS in stack dumps. const int StackColumns = 4; // Number of columns in stack dump. #define ONEK 1024 #define SIXTYFOURK (64*ONEK) #define ONEM (ONEK*ONEK) #define ONEG (ONEK*ONEK*ONEK) /////////////////////////////////////////////////////////////////////////////// // lstrrchr (avoid the C Runtime ) static TCHAR * lstrrchr(LPCTSTR string, int ch) { TCHAR *start = (TCHAR *)string; while (string && *string++) /* find end of string */ ; /* search towards front */ while (--string != start && *string != (TCHAR) ch) ; if (*string == (TCHAR) ch) /* char found ? */ return (TCHAR *)string; return NULL; } /////////////////////////////////////////////////////////////////////////////// // hprintf behaves similarly to printf, with a few vital differences. // It uses wvsprintf to do the formatting, which is a system routine, // thus avoiding C run time interactions. For similar reasons it // uses WriteFile rather than fwrite. // The one limitation that this imposes is that wvsprintf, and // therefore hprintf, cannot handle floating point numbers. // Too many calls to WriteFile can take a long time, causing // confusing delays when programs crash. Therefore I implemented // a simple buffering scheme for hprintf #define HPRINTF_BUFFER_SIZE (8*1024) // must be at least 2048 static wchar_t hprintf_buffer[HPRINTF_BUFFER_SIZE]; // wvsprintf never prints more than one K. static int hprintf_index = 0; /////////////////////////////////////////////////////////////////////////////// // hflush static void hflush(HANDLE LogFile) { if (hprintf_index > 0) { DWORD NumBytes = 0; WriteFile(LogFile, hprintf_buffer, lstrlenW(hprintf_buffer)*2, &NumBytes, 0); hprintf_index = 0; } } /////////////////////////////////////////////////////////////////////////////// // hprintf static void hprintf(HANDLE LogFile, const wchar_t *Format, ...) { if (hprintf_index > (HPRINTF_BUFFER_SIZE-1024)) { DWORD NumBytes = 0; WriteFile(LogFile, hprintf_buffer, lstrlen(hprintf_buffer)*2, &NumBytes, 0); hprintf_index = 0; } va_list arglist; va_start( arglist, Format); hprintf_index += vswprintf(&hprintf_buffer[hprintf_index], Format, arglist); va_end( arglist); } #include /////////////////////////////////////////////////////////////////////////////// // DumpMiniDump static BOOL DumpMiniDump(HANDLE hFile, PEXCEPTION_POINTERS excpInfo) { if (excpInfo == NULL) { // Generate exception to get proper context in dump __try { //OutputDebugString(_T("raising exception\r\n")); RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL); } __except(DumpMiniDump(hFile, GetExceptionInformation()), EXCEPTION_CONTINUE_EXECUTION) { } } else { //OutputDebugString(_T("writing minidump\r\n")); MINIDUMP_EXCEPTION_INFORMATION eInfo = {0}; eInfo.ThreadId = GetCurrentThreadId(); eInfo.ExceptionPointers = excpInfo; eInfo.ClientPointers = FALSE; // try to load dbghelpdll HMODULE hm = NULL; // first from app folder wchar_t szDbgHelpPath[_MAX_PATH] = {0}; if (GetModuleFileNameW( NULL, szDbgHelpPath, _MAX_PATH )) { wchar_t *pSlash = wcsrchr( szDbgHelpPath, L'\\' ); if (pSlash) { StringCchCopy( pSlash+1, _MAX_PATH, L"dbghelp.dll"); hm = LoadLibraryW( szDbgHelpPath ); } } if (!hm) { // load any version we can hm = LoadLibraryW(L"dbghelp.dll"); } if (hm) { BOOL (WINAPI* MiniDumpWriteDump)( HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, PMINIDUMP_CALLBACK_INFORMATION CallbackParam ) = NULL; //OutputDebugString(_T("Found dbghelp.dll, searching for MiniDumpWriteDump\r\n")); *(FARPROC*)&MiniDumpWriteDump = GetProcAddress(hm, "MiniDumpWriteDump"); if (MiniDumpWriteDump) { //OutputDebugString(_T("Calling MiniDumpWriteDump\r\n")); BOOL ret = MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), hFile, (MINIDUMP_TYPE)settings.dumpType, excpInfo ? &eInfo : NULL, NULL, NULL); //OutputDebugString(_T("MiniDumpWriteDump finished\r\n")); if (!ret) { DWORD le = GetLastError(); wchar_t tmp[256] = {0}; StringCchPrintfW(tmp, 256, L"call failed with error code: %d", le); //OutputDebugString(tmp); } return ret; } } } return FALSE; } /////////////////////////////////////////////////////////////////////////////// // FormatTime // // Format the specified FILETIME to output in a human readable format, // without using the C run time. static void FormatTime(LPTSTR output, FILETIME TimeToPrint) { output[0] = _T('\0'); WORD Date, Time; if (FileTimeToLocalFileTime(&TimeToPrint, &TimeToPrint) && FileTimeToDosDateTime(&TimeToPrint, &Date, &Time)) { StringCchPrintf(output, 100, _T("%d/%d/%d %02d:%02d:%02d"), (Date / 32) & 15, Date & 31, (Date / 512) + 1980, (Time >> 11), (Time >> 5) & 0x3F, (Time & 0x1F) * 2); } } /////////////////////////////////////////////////////////////////////////////// // DumpModuleInfo // // Print information about a code module (DLL or EXE) such as its size, // location, time stamp, etc. static bool DumpModuleInfo(HANDLE LogFile, HINSTANCE ModuleHandle, int nModuleNo) { bool rc = false; wchar_t szModName[MAX_PATH*2] = {0}; __try { if (GetModuleFileName(ModuleHandle, szModName, MAX_PATH*2) > 0) { // If GetModuleFileName returns greater than zero then this must // be a valid code module address. Therefore we can try to walk // our way through its structures to find the link time stamp. IMAGE_DOS_HEADER *DosHeader = (IMAGE_DOS_HEADER*)ModuleHandle; if (IMAGE_DOS_SIGNATURE != DosHeader->e_magic) return false; IMAGE_NT_HEADERS *NTHeader = (IMAGE_NT_HEADERS*)((char *)DosHeader + DosHeader->e_lfanew); if (IMAGE_NT_SIGNATURE != NTHeader->Signature) return false; // open the code module file so that we can get its file date and size HANDLE ModuleFile = CreateFile(szModName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); TCHAR TimeBuffer[100] = {0}; DWORD FileSize = 0; if (ModuleFile != INVALID_HANDLE_VALUE) { FileSize = GetFileSize(ModuleFile, 0); FILETIME LastWriteTime; if (GetFileTime(ModuleFile, 0, 0, &LastWriteTime)) { FormatTime(TimeBuffer, LastWriteTime); } CloseHandle(ModuleFile); } hprintf(LogFile, _T("Module %d\r\n"), nModuleNo); hprintf(LogFile, _T("%s\r\n"), szModName); hprintf(LogFile, _T("Image Base: 0x%08x Image Size: 0x%08x\r\n"), NTHeader->OptionalHeader.ImageBase, NTHeader->OptionalHeader.SizeOfImage), hprintf(LogFile, _T("Checksum: 0x%08x Time Stamp: 0x%08x\r\n"), NTHeader->OptionalHeader.CheckSum, NTHeader->FileHeader.TimeDateStamp); hprintf(LogFile, _T("File Size: %-10d File Time: %s\r\n"), FileSize, TimeBuffer); hprintf(LogFile, _T("Version Information:\r\n")); CMiniVersion ver(szModName); TCHAR szBuf[200] = {0}; WORD dwBuf[4] = {0}; ver.GetCompanyName(szBuf, _countof(szBuf)-1); hprintf(LogFile, _T(" Company: %s\r\n"), szBuf); ver.GetProductName(szBuf, _countof(szBuf)-1); hprintf(LogFile, _T(" Product: %s\r\n"), szBuf); ver.GetFileDescription(szBuf, _countof(szBuf)-1); hprintf(LogFile, _T(" FileDesc: %s\r\n"), szBuf); ver.GetFileVersion(dwBuf); hprintf(LogFile, _T(" FileVer: %d.%d.%d.%d\r\n"), dwBuf[0], dwBuf[1], dwBuf[2], dwBuf[3]); ver.GetProductVersion(dwBuf); hprintf(LogFile, _T(" ProdVer: %d.%d.%d.%d\r\n"), dwBuf[0], dwBuf[1], dwBuf[2], dwBuf[3]); ver.Release(); hprintf(LogFile, _T("\r\n")); rc = true; } } // Handle any exceptions by continuing from this point. __except(EXCEPTION_EXECUTE_HANDLER) { //OutputDebugString(L"DumpModuleInfo exception"); } return rc; } /////////////////////////////////////////////////////////////////////////////// // DumpModuleList // // Scan memory looking for code modules (DLLs or EXEs). VirtualQuery is used // to find all the blocks of address space that were reserved or committed, // and ShowModuleInfo will display module information if they are code // modules. static void DumpModuleList(HANDLE LogFile) { SYSTEM_INFO SystemInfo; GetSystemInfo(&SystemInfo); //OutputDebugString(L"Dumping modules list"); const size_t PageSize = SystemInfo.dwPageSize; // Set NumPages to the number of pages in the 4GByte address space, // while being careful to avoid overflowing ints const size_t NumPages = 4 * size_t(ONEG / PageSize); size_t pageNum = 0; void *LastAllocationBase = 0; int nModuleNo = 1; while (pageNum < NumPages) { MEMORY_BASIC_INFORMATION MemInfo; if (VirtualQuery((void *)(pageNum * PageSize), &MemInfo, sizeof(MemInfo))) { if (MemInfo.RegionSize > 0) { // Adjust the page number to skip over this block of memory pageNum += MemInfo.RegionSize / PageSize; if (MemInfo.State == MEM_COMMIT && MemInfo.AllocationBase > LastAllocationBase) { // Look for new blocks of committed memory, and try // recording their module names - this will fail // gracefully if they aren't code modules LastAllocationBase = MemInfo.AllocationBase; if (DumpModuleInfo(LogFile, (HINSTANCE)LastAllocationBase, nModuleNo)) { nModuleNo++; } } } else pageNum += SIXTYFOURK / PageSize; } else pageNum += SIXTYFOURK / PageSize; // If VirtualQuery fails we advance by 64K because that is the // granularity of address space doled out by VirtualAlloc() } } /////////////////////////////////////////////////////////////////////////////// // DumpSystemInformation // // Record information about the user's system, such as processor type, amount // of memory, etc. static void DumpSystemInformation(HANDLE LogFile) { FILETIME CurrentTime; GetSystemTimeAsFileTime(&CurrentTime); TCHAR szTimeBuffer[100] = {0}; FormatTime(szTimeBuffer, CurrentTime); hprintf(LogFile, _T("Error occurred at %s.\r\n"), szTimeBuffer); TCHAR szModuleName[MAX_PATH*2] = {0}; if (GetModuleFileName(0, szModuleName, _countof(szModuleName)-2) <= 0) StringCbCopy(szModuleName, sizeof(szModuleName), _T("Unknown")); TCHAR szUserName[200] = {0}; DWORD UserNameSize = _countof(szUserName)-2; if (!GetUserName(szUserName, &UserNameSize)) StringCbCopy(szUserName, sizeof(szUserName), _T("Unknown")); hprintf(LogFile, _T("%s, run by %s.\r\n"), szModuleName, szUserName); // print out operating system TCHAR szWinVer[50] = {0}, szMajorMinorBuild[50] = {0}; int nWinVer = 0; GetWinVer(szWinVer, &nWinVer, szMajorMinorBuild); hprintf(LogFile, _T("Operating system: %s (%s).\r\n"), szWinVer, szMajorMinorBuild); SYSTEM_INFO SystemInfo; GetSystemInfo(&SystemInfo); hprintf(LogFile, _T("%d processor(s), type %d.\r\n"), SystemInfo.dwNumberOfProcessors, SystemInfo.dwProcessorType); MEMORYSTATUS MemInfo; MemInfo.dwLength = sizeof(MemInfo); GlobalMemoryStatus(&MemInfo); // Print out info on memory, rounded up. hprintf(LogFile, _T("%d%% memory in use.\r\n"), MemInfo.dwMemoryLoad); hprintf(LogFile, _T("%d MBytes physical memory.\r\n"), (MemInfo.dwTotalPhys + ONEM - 1) / ONEM); hprintf(LogFile, _T("%d MBytes physical memory free.\r\n"), (MemInfo.dwAvailPhys + ONEM - 1) / ONEM); hprintf(LogFile, _T("%d MBytes paging file.\r\n"), (MemInfo.dwTotalPageFile + ONEM - 1) / ONEM); hprintf(LogFile, _T("%d MBytes paging file free.\r\n"), (MemInfo.dwAvailPageFile + ONEM - 1) / ONEM); hprintf(LogFile, _T("%d MBytes user address space.\r\n"), (MemInfo.dwTotalVirtual + ONEM - 1) / ONEM); hprintf(LogFile, _T("%d MBytes user address space free.\r\n"), (MemInfo.dwAvailVirtual + ONEM - 1) / ONEM); } /////////////////////////////////////////////////////////////////////////////// // GetExceptionDescription // // Translate the exception code into something human readable static const TCHAR *GetExceptionDescription(DWORD ExceptionCode) { struct ExceptionNames { DWORD ExceptionCode; TCHAR * ExceptionName; }; #if 0 // from winnt.h #define STATUS_WAIT_0 ((DWORD )0x00000000L) #define STATUS_ABANDONED_WAIT_0 ((DWORD )0x00000080L) #define STATUS_USER_APC ((DWORD )0x000000C0L) #define STATUS_TIMEOUT ((DWORD )0x00000102L) #define STATUS_PENDING ((DWORD )0x00000103L) #define STATUS_SEGMENT_NOTIFICATION ((DWORD )0x40000005L) #define STATUS_GUARD_PAGE_VIOLATION ((DWORD )0x80000001L) #define STATUS_DATATYPE_MISALIGNMENT ((DWORD )0x80000002L) #define STATUS_BREAKPOINT ((DWORD )0x80000003L) #define STATUS_SINGLE_STEP ((DWORD )0x80000004L) #define STATUS_ACCESS_VIOLATION ((DWORD )0xC0000005L) #define STATUS_IN_PAGE_ERROR ((DWORD )0xC0000006L) #define STATUS_INVALID_HANDLE ((DWORD )0xC0000008L) #define STATUS_NO_MEMORY ((DWORD )0xC0000017L) #define STATUS_ILLEGAL_INSTRUCTION ((DWORD )0xC000001DL) #define STATUS_NONCONTINUABLE_EXCEPTION ((DWORD )0xC0000025L) #define STATUS_INVALID_DISPOSITION ((DWORD )0xC0000026L) #define STATUS_ARRAY_BOUNDS_EXCEEDED ((DWORD )0xC000008CL) #define STATUS_FLOAT_DENORMAL_OPERAND ((DWORD )0xC000008DL) #define STATUS_FLOAT_DIVIDE_BY_ZERO ((DWORD )0xC000008EL) #define STATUS_FLOAT_INEXACT_RESULT ((DWORD )0xC000008FL) #define STATUS_FLOAT_INVALID_OPERATION ((DWORD )0xC0000090L) #define STATUS_FLOAT_OVERFLOW ((DWORD )0xC0000091L) #define STATUS_FLOAT_STACK_CHECK ((DWORD )0xC0000092L) #define STATUS_FLOAT_UNDERFLOW ((DWORD )0xC0000093L) #define STATUS_INTEGER_DIVIDE_BY_ZERO ((DWORD )0xC0000094L) #define STATUS_INTEGER_OVERFLOW ((DWORD )0xC0000095L) #define STATUS_PRIVILEGED_INSTRUCTION ((DWORD )0xC0000096L) #define STATUS_STACK_OVERFLOW ((DWORD )0xC00000FDL) #define STATUS_CONTROL_C_EXIT ((DWORD )0xC000013AL) #define STATUS_FLOAT_MULTIPLE_FAULTS ((DWORD )0xC00002B4L) #define STATUS_FLOAT_MULTIPLE_TRAPS ((DWORD )0xC00002B5L) #define STATUS_ILLEGAL_VLM_REFERENCE ((DWORD )0xC00002C0L) #endif ExceptionNames ExceptionMap[] = { {0x40010005, _T("a Control-C")}, {0x40010008, _T("a Control-Break")}, {0x80000002, _T("a Datatype Misalignment")}, {0x80000003, _T("a Breakpoint")}, {0xc0000005, _T("an Access Violation")}, {0xc0000006, _T("an In Page Error")}, {0xc0000017, _T("a No Memory")}, {0xc000001d, _T("an Illegal Instruction")}, {0xc0000025, _T("a Noncontinuable Exception")}, {0xc0000026, _T("an Invalid Disposition")}, {0xc000008c, _T("a Array Bounds Exceeded")}, {0xc000008d, _T("a Float Denormal Operand")}, {0xc000008e, _T("a Float Divide by Zero")}, {0xc000008f, _T("a Float Inexact Result")}, {0xc0000090, _T("a Float Invalid Operation")}, {0xc0000091, _T("a Float Overflow")}, {0xc0000092, _T("a Float Stack Check")}, {0xc0000093, _T("a Float Underflow")}, {0xc0000094, _T("an Integer Divide by Zero")}, {0xc0000095, _T("an Integer Overflow")}, {0xc0000096, _T("a Privileged Instruction")}, {0xc00000fD, _T("a Stack Overflow")}, {0xc0000142, _T("a DLL Initialization Failed")}, {0xe06d7363, _T("a Microsoft C++ Exception")}, }; for (int i = 0; i < _countof(ExceptionMap); i++) if (ExceptionCode == ExceptionMap[i].ExceptionCode) return ExceptionMap[i].ExceptionName; return _T("an Unknown exception type"); } /////////////////////////////////////////////////////////////////////////////// // GetFilePart static TCHAR * GetFilePart(LPCTSTR source) { TCHAR *result = lstrrchr(source, _T('\\')); if (result) result++; else result = (TCHAR *)source; return result; } #ifdef _M_IX86 /////////////////////////////////////////////////////////////////////////////// // DumpStack static void DumpStack(HANDLE LogFile, DWORD *pStack) { hprintf(LogFile, _T("\r\n\r\nStack:\r\n")); __try { // Esp contains the bottom of the stack, or at least the bottom of // the currently used area. DWORD* pStackTop; __asm { // Load the top (highest address) of the stack from the // thread information block. It will be found there in // Win9x and Windows NT. mov eax, fs:[4] mov pStackTop, eax } if (pStackTop > pStack + MaxStackDump) pStackTop = pStack + MaxStackDump; int Count = 0; DWORD* pStackStart = pStack; int nDwordsPrinted = 0; while (pStack + 1 <= pStackTop) { if ((Count % StackColumns) == 0) { pStackStart = pStack; nDwordsPrinted = 0; hprintf(LogFile, _T("0x%08x: "), pStack); } hprintf(LogFile, _T("%08x "), pStack); if ((++Count % StackColumns) == 0 || pStack + 2 > pStackTop) { nDwordsPrinted++; int n = nDwordsPrinted; while (n < 4) { hprintf(LogFile, _T(" ")); n++; } for (int i = 0; i < nDwordsPrinted; i++) { DWORD dwStack = *pStackStart; for (int j = 0; j < 4; j++) { char c = (char)(dwStack & 0xFF); if (c < 0x20 || c > 0x7E) c = '.'; #ifdef _UNICODE WCHAR w = (WCHAR)c; hprintf(LogFile, _T("%c"), w); #else hprintf(LogFile, _T("%c"), c); #endif dwStack = dwStack >> 8; } pStackStart++; } hprintf(LogFile, _T("\r\n")); } else { // hprintf(LogFile, _T("%08x "), *pStack); nDwordsPrinted++; } pStack++; } hprintf(LogFile, _T("\r\n")); } __except(EXCEPTION_EXECUTE_HANDLER) { hprintf(LogFile, _T("Exception encountered during stack dump.\r\n")); } } /////////////////////////////////////////////////////////////////////////////// // DumpRegisters static void DumpRegisters(HANDLE LogFile, PCONTEXT Context) { // Print out the register values in an XP error window compatible format. hprintf(LogFile, _T("\r\n")); hprintf(LogFile, _T("Context:\r\n")); hprintf(LogFile, _T("EDI: 0x%08x ESI: 0x%08x EAX: 0x%08x\r\n"), Context->Edi, Context->Esi, Context->Eax); hprintf(LogFile, _T("EBX: 0x%08x ECX: 0x%08x EDX: 0x%08x\r\n"), Context->Ebx, Context->Ecx, Context->Edx); hprintf(LogFile, _T("EIP: 0x%08x EBP: 0x%08x SegCs: 0x%08x\r\n"), Context->Eip, Context->Ebp, Context->SegCs); hprintf(LogFile, _T("EFlags: 0x%08x ESP: 0x%08x SegSs: 0x%08x\r\n"), Context->EFlags, Context->Esp, Context->SegSs); } #endif BOOL CreateLog(PEXCEPTION_POINTERS pExceptPtrs, LPCWSTR lpszMessage) { HANDLE hLogFile = CreateFile(settings.logPath, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, 0); if (hLogFile == INVALID_HANDLE_VALUE) { //OutputDebugString(_T("Error creating exception report\r\n")); return FALSE; } // add BOM WORD wBOM = 0xFEFF; DWORD num = 0; WriteFile(hLogFile, &wBOM, sizeof(WORD), &num, NULL); // Append to the error log SetFilePointer(hLogFile, 0, 0, FILE_END); wchar_t line[1024] = {0}; wchar_t msgBody[4*1024] = {0}; wchar_t winampVersionWide[1024] = {0}; MultiByteToWideCharSZ(CP_ACP, 0, winampVersion, -1, winampVersionWide, 1024); StringCchPrintf(line, 1024, L"Winamp client version: %s\r\n", winampVersionWide); StringCchCopy(msgBody, 4*1024, line); PEXCEPTION_RECORD Exception = pExceptPtrs->ExceptionRecord; PCONTEXT Context = pExceptPtrs->ContextRecord; TCHAR szCrashModulePathName[MAX_PATH*2] = {0}; TCHAR *pszCrashModuleFileName = _T("Unknown"); #ifdef _M_IX86 MEMORY_BASIC_INFORMATION MemInfo; // VirtualQuery can be used to get the allocation base associated with a // code address, which is the same as the ModuleHandle. This can be used // to get the filename of the module that the crash happened in. if (VirtualQuery((void*)Context->Eip, &MemInfo, sizeof(MemInfo)) && (GetModuleFileName((HINSTANCE)MemInfo.AllocationBase, szCrashModulePathName, sizeof(szCrashModulePathName)-2) > 0)) { //OutputDebugString(szCrashModulePathName); pszCrashModuleFileName = GetFilePart(szCrashModulePathName); } #endif // Print out the beginning of the error log in a Win95 error window // compatible format. TCHAR szModuleName[MAX_PATH*2] = {0}; if (GetModuleFileName(0, szModuleName, _countof(szModuleName)-2) <= 0) StringCbCopy(szModuleName, sizeof(szModuleName), _T("Unknown")); TCHAR *pszFilePart = GetFilePart(szModuleName); // Extract the file name portion and remove it's file extension TCHAR szFileName[MAX_PATH*2] = {0}; StringCbCopy(szFileName, sizeof(szFileName), pszFilePart); TCHAR *lastperiod = lstrrchr(szFileName, _T('.')); if (lastperiod) lastperiod[0] = 0; #ifdef _M_IX86 StringCchPrintf(line, 1024, L"%s caused %s (0x%08x) \r\nin module %s at %04x:%08x.\r\n\r\n", szFileName, GetExceptionDescription(Exception->ExceptionCode), Exception->ExceptionCode, pszCrashModuleFileName, Context->SegCs, Context->Eip); #endif StringCchCat(msgBody, 4*1024, line); StringCchPrintf(line, 1024, L"Exception handler called in %s.\r\n", lpszMessage); StringCchCat(msgBody, 4*1024, line); hprintf(hLogFile, L"%s", msgBody); wchar_t *p = msgBody, *end = msgBody + wcslen(msgBody); while(p != end) { if (*p == L'\r') *p = 1; if (*p == L'\n') *p = 2; p++; } settings.WriteBody(msgBody); if (settings.logSystem) { DumpSystemInformation(hLogFile); // If the exception was an access violation, print out some additional // information, to the error log and the debugger. if (Exception->ExceptionCode == STATUS_ACCESS_VIOLATION && Exception->NumberParameters >= 2) { TCHAR szDebugMessage[1000] = {0}; const TCHAR* readwrite = _T("Read from"); if (Exception->ExceptionInformation[0]) readwrite = _T("Write to"); StringCchPrintf(szDebugMessage, 1000, _T("%s location %08x caused an access violation.\r\n"), readwrite, Exception->ExceptionInformation[1]); hprintf(hLogFile, _T("%s"), szDebugMessage); } } if (settings.logRegistry) { #ifdef _M_IX86 DumpRegisters(hLogFile, Context); #endif // Print out the bytes of code at the instruction pointer. Since the // crash may have been caused by an instruction pointer that was bad, // this code needs to be wrapped in an exception handler, in case there // is no memory to read. If the dereferencing of code[] fails, the // exception handler will print '??'. #ifdef _M_IX86 hprintf(hLogFile, _T("\r\nBytes at CS:EIP:\r\n")); BYTE * code = (BYTE *)Context->Eip; for (int codebyte = 0; codebyte < NumCodeBytes; codebyte++) { __try { hprintf(hLogFile, _T("%02x "), code[codebyte]); } __except(EXCEPTION_EXECUTE_HANDLER) { hprintf(hLogFile, _T("?? ")); } } #endif } if (settings.logStack) { // Time to print part or all of the stack to the error log. This allows // us to figure out the call stack, parameters, local variables, etc. // Esp contains the bottom of the stack, or at least the bottom of // the currently used area #ifdef _M_IX86 DWORD* pStack = (DWORD *)Context->Esp; DumpStack(hLogFile, pStack); #endif } if (settings.logModule) { DumpModuleList(hLogFile); } hprintf(hLogFile, _T("\r\n===== [end of log file] =====\r\n")); hflush(hLogFile); CloseHandle(hLogFile); return TRUE; } BOOL CreateDump(PEXCEPTION_POINTERS pExceptPtrs) { BOOL retCode = FALSE; // Create the file //OutputDebugString(_T("CreateFile: ")); //OutputDebugString(settings.dumpPath); HANDLE hMiniDumpFile = CreateFile( settings.dumpPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL); // Write the minidump to the file if (hMiniDumpFile != INVALID_HANDLE_VALUE) { retCode = DumpMiniDump(hMiniDumpFile, pExceptPtrs); // Close file CloseHandle(hMiniDumpFile); if (!retCode) DeleteFile(settings.dumpPath); } return retCode; }