Archived
1
0
This repository has been archived on 2024-10-17. You can view files and clone it, but cannot push or open issues or pull requests.
winamp/Src/Plugins/General/gen_crasher/ExceptionHandler.cpp

814 lines
25 KiB
C++
Raw Normal View History

2024-09-24 12:54:57 +00:00
// ExceptionHandler.cpp Version 1.4
//
// Copyright <20> 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 <tchar.h>
#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 <strsafe.h>
///////////////////////////////////////////////////////////////////////////////
// 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;
}