Blame |
Last modification |
View Log
| RSS feed
#include "SEHCatcher.h"
#include "SexyAppBase.h"
#include <fstream>
#include <process.h>
#ifdef ZYLOM
#include "zylomso.h"
using namespace zylom::zylomso;
#endif
using namespace Sexy;
LPTOP_LEVEL_EXCEPTION_FILTER SEHCatcher::mPreviousFilter;
SexyAppBase* SEHCatcher::mApp = NULL;
HFONT SEHCatcher::mDialogFont = NULL;
HFONT SEHCatcher::mBoldFont = NULL;
bool SEHCatcher::mHasDemoFile = false;
bool SEHCatcher::mDebugError = false;
std::string SEHCatcher::mErrorTitle;
std::string SEHCatcher::mErrorText;
std::string SEHCatcher::mUserText;
std::string SEHCatcher::mUploadFileName;
HWND SEHCatcher::mYesButtonWindow = NULL;
HWND SEHCatcher::mNoButtonWindow = NULL;
HWND SEHCatcher::mDebugButtonWindow = NULL;
HWND SEHCatcher::mEditWindow = NULL;
HMODULE SEHCatcher::mImageHelpLib = NULL;
SYMINITIALIZEPROC SEHCatcher::mSymInitialize = NULL;
SYMSETOPTIONSPROC SEHCatcher::mSymSetOptions = NULL;
UNDECORATESYMBOLNAMEPROC SEHCatcher::mUnDecorateSymbolName = NULL;
SYMCLEANUPPROC SEHCatcher::mSymCleanup = NULL;
STACKWALKPROC SEHCatcher::mStackWalk = NULL;
SYMFUNCTIONTABLEACCESSPROC SEHCatcher::mSymFunctionTableAccess = NULL;
SYMGETMODULEBASEPROC SEHCatcher::mSymGetModuleBase = NULL;
SYMGETSYMFROMADDRPROC SEHCatcher::mSymGetSymFromAddr = NULL;
HTTPTransfer SEHCatcher::mSubmitReportTransfer;
bool SEHCatcher::mExiting = false;
bool SEHCatcher::mShowUI = true;
bool SEHCatcher::mAllowSubmit = true;
std::wstring SEHCatcher::mCrashMessage = L"An unexpected error has occured!";
std::string SEHCatcher::mSubmitHost;
std::wstring SEHCatcher::mSubmitMessage = L"Please help out by providing as much information as you can about this crash.";
std::wstring SEHCatcher::mSubmitErrorMessage = L"Failed to connect to server.";
static bool gUseDefaultFonts = true;
struct
{
DWORD dwExceptionCode;
char *szMessage;
} gMsgTable[] = {
{ STATUS_SEGMENT_NOTIFICATION, "Segment Notification" },
{ STATUS_BREAKPOINT, "Breakpoint" },
{ STATUS_SINGLE_STEP, "Single step" },
{ STATUS_WAIT_0, "Wait 0" },
{ STATUS_ABANDONED_WAIT_0, "Abandoned Wait 0" },
{ STATUS_USER_APC, "User APC" },
{ STATUS_TIMEOUT, "Timeout" },
{ STATUS_PENDING, "Pending" },
{ STATUS_GUARD_PAGE_VIOLATION, "Guard Page Violation" },
{ STATUS_DATATYPE_MISALIGNMENT, "Data Type Misalignment" },
{ STATUS_ACCESS_VIOLATION, "Access Violation" },
{ STATUS_IN_PAGE_ERROR, "In Page Error" },
{ STATUS_NO_MEMORY, "No Memory" },
{ STATUS_ILLEGAL_INSTRUCTION, "Illegal Instruction" },
{ STATUS_NONCONTINUABLE_EXCEPTION, "Noncontinuable Exception" },
{ STATUS_INVALID_DISPOSITION, "Invalid Disposition" },
{ STATUS_ARRAY_BOUNDS_EXCEEDED, "Array Bounds Exceeded" },
{ STATUS_FLOAT_DENORMAL_OPERAND, "Float Denormal Operand" },
{ STATUS_FLOAT_DIVIDE_BY_ZERO, "Divide by Zero" },
{ STATUS_FLOAT_INEXACT_RESULT, "Float Inexact Result" },
{ STATUS_FLOAT_INVALID_OPERATION, "Float Invalid Operation" },
{ STATUS_FLOAT_OVERFLOW, "Float Overflow" },
{ STATUS_FLOAT_STACK_CHECK, "Float Stack Check" },
{ STATUS_FLOAT_UNDERFLOW, "Float Underflow" },
{ STATUS_INTEGER_DIVIDE_BY_ZERO, "Integer Divide by Zero" },
{ STATUS_INTEGER_OVERFLOW, "Integer Overflow" },
{ STATUS_PRIVILEGED_INSTRUCTION, "Privileged Instruction" },
{ STATUS_STACK_OVERFLOW, "Stack Overflow" },
{ STATUS_CONTROL_C_EXIT, "Ctrl+C Exit" },
{ 0xFFFFFFFF, "" }
};
SEHCatcher::SEHCatcher()
{
mPreviousFilter = SetUnhandledExceptionFilter(UnhandledExceptionFilter);
}
SEHCatcher::~SEHCatcher()
{
SetUnhandledExceptionFilter(mPreviousFilter);
}
long __stdcall SEHCatcher::UnhandledExceptionFilter(LPEXCEPTION_POINTERS lpExceptPtr)
{
if (mApp != NULL)
mApp->SEHOccured();
DoHandleDebugEvent(lpExceptPtr);
if (!mDebugError)
SetErrorMode(SEM_NOGPFAULTERRORBOX);
return EXCEPTION_CONTINUE_SEARCH;
}
bool SEHCatcher::LoadImageHelp()
{
mImageHelpLib = LoadLibraryA("IMAGEHLP.DLL");
if (!mImageHelpLib)
return false;
mSymInitialize = (SYMINITIALIZEPROC) GetProcAddress(mImageHelpLib, "SymInitialize");
if (!mSymInitialize)
return false;
mSymSetOptions = (SYMSETOPTIONSPROC) GetProcAddress(mImageHelpLib, "SymSetOptions");
if (!mSymSetOptions)
return false;
mSymCleanup = (SYMCLEANUPPROC) GetProcAddress(mImageHelpLib, "SymCleanup");
if (!mSymCleanup)
return false;
mUnDecorateSymbolName = (UNDECORATESYMBOLNAMEPROC) GetProcAddress(mImageHelpLib, "UnDecorateSymbolName");
if (!mUnDecorateSymbolName)
return false;
mStackWalk = (STACKWALKPROC) GetProcAddress(mImageHelpLib, "StackWalk");
if (!mStackWalk)
return false;
mSymFunctionTableAccess = (SYMFUNCTIONTABLEACCESSPROC) GetProcAddress(mImageHelpLib, "SymFunctionTableAccess");
if (!mSymFunctionTableAccess)
return false;
mSymGetModuleBase = (SYMGETMODULEBASEPROC) GetProcAddress(mImageHelpLib, "SymGetModuleBase");
if (!mSymGetModuleBase)
return false;
mSymGetSymFromAddr = (SYMGETSYMFROMADDRPROC) GetProcAddress(mImageHelpLib, "SymGetSymFromAddr" );
if (!mSymGetSymFromAddr)
return false;
mSymSetOptions(SYMOPT_DEFERRED_LOADS);
// Get image filename of the main executable
char filepath[MAX_PATH], *lastdir, *pPath;
DWORD filepathlen = GetModuleFileNameA ( NULL, filepath, sizeof(filepath));
lastdir = strrchr (filepath, '/');
if (lastdir == NULL) lastdir = strrchr (filepath, '\\');
if (lastdir != NULL) lastdir[0] = '\0';
// Initialize the symbol table routines, supplying a pointer to the path
pPath = filepath;
if (strlen (filepath) == 0) pPath = NULL;
if (!mSymInitialize (GetCurrentProcess(), pPath, TRUE))
return false;
return true;
}
void SEHCatcher::UnloadImageHelp()
{
if (mImageHelpLib != NULL)
FreeLibrary(mImageHelpLib);
}
static bool StrToLongHex(const std::string& aString, DWORD* theValue)
{
*theValue = 0;
for (int i = 0; i < (int)aString.length(); i++)
{
char aChar = aString[i];
int aValue;
if ((aChar >= '0') && (aChar <= '9'))
aValue = aChar - '0';
else if ((aChar >= 'A') && (aChar <= 'F'))
aValue = (aChar - 'A') + 10;
else if ((aChar >= 'a') && (aChar <= 'f'))
aValue = (aChar - 'a') + 10;
else
return false;
*theValue += aValue << ((aString.length()-i-1)*4);
}
return true;
}
void SEHCatcher::GetSymbolsFromMapFile(std::string &theDebugDump)
{
DWORD aTick = GetTickCount();
WIN32_FIND_DATAA aFindData;
HANDLE aFindHandle = FindFirstFileA("*.map",&aFindData);
if (aFindHandle==INVALID_HANDLE_VALUE)
return;
FindClose(aFindHandle);
typedef std::pair<DWORD,DWORD> SymbolPair;
typedef std::map<SymbolPair,std::string> SymbolMap;
typedef std::pair<std::string,int> LineNumPair;
typedef std::map<SymbolPair,LineNumPair> LineNumMap;
/**/
SymbolMap aSymbolMap;
LineNumMap aLineNumMap;
std::fstream aFile(aFindData.cFileName, std::ios::in);
if (!aFile.is_open())
return;
// Parse map file
std::string aCurLinesFile;
while (!aFile.eof())
{
char aStr[4096];
aFile.getline(aStr, 4096);
std::string aString = aStr;
if ((aString.length() > 22) && (aString[5] == ':'))
{
std::string aFoundPreStr = aString.substr(1, 4);
std::string aFoundPostStr = aString.substr(6, 8);
DWORD aFoundPreVal;
DWORD aFoundPostVal;
if (StrToLongHex(aFoundPreStr, &aFoundPreVal) && StrToLongHex(aFoundPostStr, &aFoundPostVal))
{
int aSpacePos = aString.find(' ', 21);
if ((aString[20] == ' ') && (aSpacePos >= 0))
aSymbolMap[SymbolPair(aFoundPreVal,aFoundPostVal)] = aString.substr(21, aSpacePos-21);
}
}
else if (strcmp(aString.substr(0, strlen("Line numbers")).c_str(), "Line numbers") == 0)
{
int aSegmentPos = aString.rfind(')');
if (aSegmentPos == -1)
aSegmentPos = aString.length();
int aPreLen = strlen("Line numbers for ");
int aStartPos = aString.find('(');
aCurLinesFile = aString.substr(aStartPos + 1, aSegmentPos - aStartPos - 1);
}
else if ((aCurLinesFile.length() > 0) && (aString.length() == 80))
{
// Line number stuff
for (int i = 0; i < 4; i++)
{
int aStartLoc = 20 * i;
int aLine = atoi(aString.substr(aStartLoc, 6).c_str());
std::string aFoundPreStr = aString.substr(aStartLoc + 7, 4);
std::string aFoundPostStr = aString.substr(aStartLoc + 12, 8);
DWORD aFoundPreVal;
DWORD aFoundPostVal;
if (StrToLongHex(aFoundPreStr, &aFoundPreVal) && StrToLongHex(aFoundPostStr, &aFoundPostVal))
{
aLineNumMap[SymbolPair(aFoundPreVal,aFoundPostVal)] = LineNumPair(aCurLinesFile,aLine);
}
}
}
}
// Parse stack trace
for (int i = 0; i < (int)theDebugDump.length(); i++)
{
if (theDebugDump.at(i) == ':')
{
std::string aFindPreStr = theDebugDump.substr(i-4, 4);
std::string aFindPostStr = theDebugDump.substr(i+1, 8);
DWORD aFindPreVal;
DWORD aFindPostVal;
if (StrToLongHex(aFindPreStr, &aFindPreVal) && StrToLongHex(aFindPostStr, &aFindPostVal))
{
int aBestDist = -1;
SymbolMap::iterator aSymbolItr = aSymbolMap.lower_bound(SymbolPair(aFindPreVal,aFindPostVal));
if (aSymbolItr!=aSymbolMap.end() && aSymbolItr!=aSymbolMap.begin() && aSymbolItr->first.second!=aFindPostVal)
--aSymbolItr;
if (aSymbolItr!=aSymbolMap.end() && aSymbolItr->first.first==aFindPreVal)
aBestDist = aFindPostVal - aSymbolItr->first.second;
if (aBestDist != -1)
{
std::string &aBestName = aSymbolItr->second;
char aSymbolName[4096];
if (mUnDecorateSymbolName(aBestName.c_str(), aSymbolName, 4096,
UNDNAME_NO_ALLOCATION_MODEL | UNDNAME_NO_ACCESS_SPECIFIERS |
UNDNAME_NO_THROW_SIGNATURES | UNDNAME_NO_MEMBER_TYPE) == 0)
strcpy(aSymbolName, aBestName.c_str());
if (aBestDist != 0)
{
sprintf(aSymbolName+strlen(aSymbolName), "+0x%X", aBestDist);
}
std::string aNewText = aSymbolName;
LineNumMap::iterator aLineNumItr = aLineNumMap.lower_bound(SymbolPair(aFindPreVal,aFindPostVal));
if (aLineNumItr!=aLineNumMap.end() && aLineNumItr!=aLineNumMap.begin() && aLineNumItr->first.second!=aFindPostVal)
--aLineNumItr;
if (aLineNumItr!=aLineNumMap.end() && aLineNumItr->first.first==aFindPreVal)
{
std::string &aBestFile = aLineNumItr->second.first;
int aBestLine = aLineNumItr->second.second;
int aBestLineDist = aFindPostVal - aLineNumItr->first.second;
char aDistStr[4096];
sprintf(aDistStr, "\r\n %s line %d +0x%X", aBestFile.c_str(), aBestLine, aBestLineDist);
aNewText += aDistStr;
}
theDebugDump.erase(i-4, 13);
theDebugDump.insert(i-4, aNewText.c_str());
}
}
}
}
// MessageBox(NULL,StrFormat("%d",GetTickCount()-aTick).c_str(),"Time",MB_OK);
}
void SEHCatcher::DoHandleDebugEvent(LPEXCEPTION_POINTERS lpEP)
{
bool hasImageHelp = LoadImageHelp();
std::string anErrorTitle;
std::string aDebugDump;
char aBuffer[2048];
///////////////////////////
// first name the exception
char *szName = NULL;
for (int i=0; gMsgTable[i].dwExceptionCode != 0xFFFFFFFF; i++)
{
if (gMsgTable[i].dwExceptionCode == lpEP->ExceptionRecord->ExceptionCode)
{
szName = gMsgTable[i].szMessage;
break;
}
}
if (szName != NULL)
{
sprintf(aBuffer,"Exception: %s (code 0x%x) at address %08X in thread %X\r\n",
szName, lpEP->ExceptionRecord->ExceptionCode,
lpEP->ExceptionRecord->ExceptionAddress, GetCurrentThreadId());
}
else
{
sprintf(aBuffer,"Unknown exception: (code 0x%x) at address %08X in thread %X\r\n",
lpEP->ExceptionRecord->ExceptionCode,
lpEP->ExceptionRecord->ExceptionAddress, GetCurrentThreadId());
}
aDebugDump += aBuffer;
///////////////////////////////////////////////////////////
// Get logical address of the module where exception occurs
DWORD section, offset;
GetLogicalAddress(lpEP->ExceptionRecord->ExceptionAddress, aBuffer, sizeof(aBuffer), section, offset);
aDebugDump += "Module: " + GetFilename(aBuffer) + "\r\n";
sprintf(aBuffer, "Logical Address: %04X:%08X\r\n", section, offset);
aDebugDump += aBuffer;
aDebugDump += "\r\n";
anErrorTitle = StrFormat("Exception at %04X:%08X", section, offset);
std::string aWalkString;
if (hasImageHelp)
aWalkString = ImageHelpWalk(lpEP->ContextRecord, 0);
if (aWalkString.length() == 0)
aWalkString = IntelWalk(lpEP->ContextRecord, 0);
aDebugDump += aWalkString;
aDebugDump += "\r\n";
sprintf(aBuffer, ("EAX:%08X EBX:%08X ECX:%08X EDX:%08X ESI:%08X EDI:%08X\r\n"),
lpEP->ContextRecord->Eax, lpEP->ContextRecord->Ebx, lpEP->ContextRecord->Ecx, lpEP->ContextRecord->Edx, lpEP->ContextRecord->Esi, lpEP->ContextRecord->Edi);
aDebugDump += aBuffer;
sprintf(aBuffer, "EIP:%08X ESP:%08X EBP:%08X\r\n", lpEP->ContextRecord->Eip, lpEP->ContextRecord->Esp, lpEP->ContextRecord->Ebp);
aDebugDump += aBuffer;
sprintf(aBuffer, "CS:%04X SS:%04X DS:%04X ES:%04X FS:%04X GS:%04X\r\n", lpEP->ContextRecord->SegCs, lpEP->ContextRecord->SegSs, lpEP->ContextRecord->SegDs, lpEP->ContextRecord->SegEs, lpEP->ContextRecord->SegFs, lpEP->ContextRecord->SegGs );
aDebugDump += aBuffer;
sprintf(aBuffer, "Flags:%08X\r\n", lpEP->ContextRecord->EFlags );
aDebugDump += aBuffer;
aDebugDump += "\r\n";
aDebugDump += GetSysInfo();
if (mApp != NULL)
{
std::string aGameSEHInfo = mApp->GetGameSEHInfo();
if (aGameSEHInfo.length() > 0)
{
aDebugDump += "\r\n";
aDebugDump += aGameSEHInfo;
}
mApp->CopyToClipboard(aDebugDump);
}
if (hasImageHelp)
GetSymbolsFromMapFile(aDebugDump);
WriteToFile(aDebugDump);
#ifdef ZYLOM
ZylomGS_StandAlone_SendBugReport((char*) aDebugDump.c_str());
#else
if (mApp != NULL)
{
if (mApp->mRecordingDemoBuffer)
{
// Make sure we have enough update block things in there to
// get to the final crashing update
mApp->WriteDemoTimingBlock();
mApp->mDemoBuffer.WriteNumBits(0, 1);
mApp->mDemoBuffer.WriteNumBits(DEMO_IDLE, 5);
mApp->WriteDemoBuffer();
mUploadFileName = mApp->mDemoFileName;
}
mHasDemoFile = mApp->mRecordingDemoBuffer;
std::string anUploadFile = mApp->NotifyCrashHook();
if (!anUploadFile.empty())
{
mUploadFileName = anUploadFile;
mHasDemoFile = true;
}
mApp->mRecordingDemoBuffer = false;
mApp->mPlayingDemoBuffer = false;
}
if (mShowUI)
ShowErrorDialog(anErrorTitle, aDebugDump);
#endif
//::MessageBox(NULL, aDebugDump.c_str(), "ERROR", MB_ICONERROR);
UnloadImageHelp();
}
std::string SEHCatcher::IntelWalk(PCONTEXT theContext, int theSkipCount)
{
std::string aDebugDump;
char aBuffer[2048];
DWORD pc = theContext->Eip;
PDWORD pFrame, pPrevFrame;
pFrame = (PDWORD)theContext->Ebp;
for (;;)
{
char szModule[MAX_PATH] = "";
DWORD section = 0, offset = 0;
GetLogicalAddress((PVOID)pc, szModule, sizeof(szModule), section, offset);
sprintf(aBuffer, "%08X %08X %04X:%08X %s\r\n",
pc, pFrame, section, offset, GetFilename(szModule).c_str());
aDebugDump += aBuffer;
pc = pFrame[1];
pPrevFrame = pFrame;
pFrame = (PDWORD)pFrame[0]; // proceed to next higher frame on stack
if ((DWORD)pFrame & 3) // Frame pointer must be aligned on a
break; // DWORD boundary. Bail if not so.
if (pFrame <= pPrevFrame)
break;
// Can two DWORDs be read from the supposed frame address?
if (IsBadWritePtr(pFrame, sizeof(PVOID)*2))
break;
};
return aDebugDump;
}
std::string SEHCatcher::ImageHelpWalk(PCONTEXT theContext, int theSkipCount)
{
char aBuffer[2048];
std::string aDebugDump;
STACKFRAME sf;
memset( &sf, 0, sizeof(sf) );
// Initialize the STACKFRAME structure for the first call. This is only
// necessary for Intel CPUs, and isn't mentioned in the documentation.
sf.AddrPC.Offset = theContext->Eip;
sf.AddrPC.Mode = AddrModeFlat;
sf.AddrStack.Offset = theContext->Esp;
sf.AddrStack.Mode = AddrModeFlat;
sf.AddrFrame.Offset = theContext->Ebp;
sf.AddrFrame.Mode = AddrModeFlat;
int aLevelCount = 0;
for (;;)
{
if (!mStackWalk(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(),
&sf, NULL /*theContext*/, NULL, mSymFunctionTableAccess, mSymGetModuleBase, 0))
{
DWORD lastErr = GetLastError();
sprintf(aBuffer, "StackWalk failed (error %d)\r\n", lastErr);
aDebugDump += aBuffer;
break;
}
if ((sf.AddrFrame.Offset == 0) || (sf.AddrPC.Offset == 0))
break;
if (theSkipCount > 0)
{
theSkipCount--;
continue;
}
BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 512];
PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)symbolBuffer;
pSymbol->SizeOfStruct = sizeof(symbolBuffer);
pSymbol->MaxNameLength = 512;
DWORD symDisplacement = 0; // Displacement of the input address,
// relative to the start of the symbol
if (mSymGetSymFromAddr(GetCurrentProcess(), sf.AddrPC.Offset, &symDisplacement, pSymbol))
{
char aUDName[256];
mUnDecorateSymbolName(pSymbol->Name, aUDName, 256,
UNDNAME_NO_ALLOCATION_MODEL | UNDNAME_NO_ALLOCATION_LANGUAGE |
UNDNAME_NO_MS_THISTYPE | UNDNAME_NO_ACCESS_SPECIFIERS |
UNDNAME_NO_THISTYPE | UNDNAME_NO_MEMBER_TYPE |
UNDNAME_NO_RETURN_UDT_MODEL | UNDNAME_NO_THROW_SIGNATURES |
UNDNAME_NO_SPECIAL_SYMS);
sprintf(aBuffer, "%08X %08X %hs+%X\r\n",
sf.AddrFrame.Offset, sf.AddrPC.Offset, aUDName, symDisplacement);
}
else // No symbol found. Print out the logical address instead.
{
char szModule[MAX_PATH];
DWORD section = 0, offset = 0;
GetLogicalAddress((PVOID)sf.AddrPC.Offset, szModule, sizeof(szModule), section, offset);
sprintf(aBuffer, "%08X %08X %04X:%08X %s\r\n", sf.AddrFrame.Offset, sf.AddrPC.Offset, section, offset, GetFilename(szModule).c_str());
}
aDebugDump += aBuffer;
sprintf(aBuffer, "Params: %08X %08X %08X %08X\r\n", sf.Params[0], sf.Params[1], sf.Params[2], sf.Params[3]);
aDebugDump += aBuffer;
aDebugDump += "\r\n";
aLevelCount++;
}
return aDebugDump;
}
bool SEHCatcher::GetLogicalAddress(void* addr, char* szModule, DWORD len, DWORD& section, DWORD& offset)
{
MEMORY_BASIC_INFORMATION mbi;
if (!VirtualQuery(addr, &mbi, sizeof(mbi)))
return false;
DWORD hMod = (DWORD)mbi.AllocationBase;
if (!GetModuleFileNameA((HMODULE) hMod, szModule, len))
return false;
// Point to the DOS header in memory
PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER) hMod;
// From the DOS header, find the NT (PE) header
PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS) (hMod + pDosHdr->e_lfanew);
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHdr);
DWORD rva = (DWORD) addr - hMod; // RVA is offset from module load address
// Iterate through the section table, looking for the one that encompasses
// the linear address.
for (unsigned i = 0; i < pNtHdr->FileHeader.NumberOfSections; i++, pSection++)
{
DWORD sectionStart = pSection->VirtualAddress;
DWORD sectionEnd = sectionStart + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);
// Is the address in this section???
if ((rva >= sectionStart) && (rva <= sectionEnd))
{
// Yes, address is in the section. Calculate section and offset,
// and store in the "section" & "offset" params, which were
// passed by reference.
section = i+1;
offset = rva - sectionStart;
return true;
}
}
return false; // Should never get here!
}
std::string SEHCatcher::GetFilename(const std::string& thePath)
{
int aLastSlash = max((int) thePath.rfind('\\'), (int) thePath.rfind('/'));
if (aLastSlash >= 0)
{
return thePath.substr(aLastSlash+1);
}
else
return thePath;
}
HWND gEditWindow = NULL;
int aCount = 0;
static LRESULT CALLBACK SEHProgressWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_COMMAND:
{
HWND hwndCtl = (HWND) lParam;
if (hwndCtl == SEHCatcher::mNoButtonWindow)
{
// Cancel it, man!
SEHCatcher::mSubmitReportTransfer.Abort();
}
}
break;
case WM_CLOSE:
SEHCatcher::mSubmitReportTransfer.Abort();
return 0;
case WM_TIMER:
{
if (wParam == 0)
{
int aResult = SEHCatcher::mSubmitReportTransfer.GetResultCode();
if ((aResult != HTTPTransfer::RESULT_NOT_COMPLETED) &&
(aResult != HTTPTransfer::RESULT_NOT_STARTED))
{
if ((aResult != HTTPTransfer::RESULT_DONE) &&
(aResult != HTTPTransfer::RESULT_ABORTED))
{
SEHCatcher::mSubmitReportTransfer.Reset();
if (::MessageBoxW(hWnd, SEHCatcher::mSubmitErrorMessage.c_str(),
L"Connection Failed", MB_RETRYCANCEL | MB_APPLMODAL) == IDRETRY)
{
_beginthread(SEHCatcher::SubmitReportThread, 0, 0);
return 0;
}
}
SEHCatcher::mExiting = true;
}
}
else if (wParam == 1)
{
++aCount;
std::string aNewString = "Please Wait";
for (int i = 0; i < (aCount % 10); i++)
aNewString += " .";
SetWindowTextA(gEditWindow, aNewString.c_str());
}
}
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
static void CreateProgressWindow()
{
WNDCLASSA wc;
wc.style = 0;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = ::GetSysColorBrush(COLOR_BTNFACE);
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wc.hIcon = ::LoadIcon(NULL, IDI_ERROR);
wc.hInstance = gHInstance;
wc.lpfnWndProc = SEHProgressWindowProc;
wc.lpszClassName = "SEHProgressWindow";
wc.lpszMenuName = NULL;
RegisterClassA(&wc);
RECT aRect;
aRect.left = 0;
aRect.top = 0;
aRect.right = 240;
aRect.bottom = 64;
DWORD aWindowStyle = WS_CLIPCHILDREN | WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
BOOL worked = AdjustWindowRect(&aRect, aWindowStyle, FALSE);
HWND aHWnd = CreateWindowA("SEHProgressWindow", "Submitting Report",
aWindowStyle,
64, 64,
aRect.right - aRect.left,
aRect.bottom - aRect.top,
NULL,
NULL,
gHInstance,
0);
// Check every 20ms to see if the transfer has completed
SetTimer(aHWnd, 0, 20, NULL);
// Every second we should change the edit text
SetTimer(aHWnd, 1, 1000, NULL);
gEditWindow = CreateWindowA("EDIT",
"Please Wait",
WS_VISIBLE | WS_CHILD | ES_READONLY,
24, 10,
240-8-8,
24,
aHWnd,
NULL,
gHInstance,
0);
if (!gUseDefaultFonts)
SendMessage(gEditWindow, WM_SETFONT, (WPARAM) SEHCatcher::mBoldFont, 0);
SEHCatcher::mNoButtonWindow = CreateWindowA("BUTTON", "Abort",
WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON,
(240 - 96)/2, 64 - 22 - 6,
96,
22,
aHWnd,
NULL,
gHInstance,
0);
if (!gUseDefaultFonts)
SendMessage(SEHCatcher::mNoButtonWindow, WM_SETFONT, (WPARAM) SEHCatcher::mDialogFont, 0);
ShowWindow(aHWnd, SW_NORMAL);
}
LRESULT CALLBACK SEHCatcher::SEHWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_COMMAND:
{
HWND hwndCtl = (HWND) lParam;
if (hwndCtl == mYesButtonWindow)
{
// Hide current window and bring up score submitting stuff
ShowWindow(hWnd, SW_HIDE);
ShowSubmitInfoDialog();
}
else if (hwndCtl == mNoButtonWindow)
{
mExiting = true;
}
else if (hwndCtl == mDebugButtonWindow)
{
mDebugError = true;
mExiting = true;
}
}
break;
case WM_CLOSE:
mExiting = true;
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK SEHCatcher::SubmitInfoWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_COMMAND:
{
HWND hwndCtl = (HWND) lParam;
if (hwndCtl == mYesButtonWindow)
{
// Hide current window and bring up score submitting stuff
ShowWindow(hWnd, SW_HIDE);
char aStr[8192];
GetWindowTextA(mEditWindow, aStr, 8192);
mUserText = aStr;
CreateProgressWindow();
_beginthread(SubmitReportThread, 0, 0);
}
else if (hwndCtl == mNoButtonWindow)
{
mExiting = true;
}
}
break;
case WM_CLOSE:
mExiting = true;
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
void SEHCatcher::WriteToFile(const std::string& theErrorText)
{
std::fstream aStream("crash.txt", std::ios::out);
aStream << theErrorText.c_str() << std::endl;
}
void SEHCatcher::SubmitReportThread(void *theArg)
{
std::string aSeperator = "---------------------------7d3e1f30eec";
DefinesMap aSEHWebParams;
mApp->GetSEHWebParams(&aSEHWebParams);
std::string aContent;
DefinesMap::iterator anItr = aSEHWebParams.begin();
while (anItr != aSEHWebParams.end())
{
aContent += "--" + aSeperator + "\r\n"
"Content-Disposition: form-data; name=\"" + anItr->first + "\"\r\n" +
"\r\n" +
anItr->second + "\r\n";
++anItr;
}
aContent +=
/*"--" + aSeperator + "\r\n"
"Content-Disposition: form-data; name=\"username\"\r\n" +
"\r\n" +
mApp->mUserName + "\r\n" + */
"--" + aSeperator + "\r\n"
"Content-Disposition: form-data; name=\"prod\"\r\n" +
"\r\n" +
mApp->mProdName + "\r\n" +
"--" + aSeperator + "\r\n"
"Content-Disposition: form-data; name=\"version\"\r\n" +
"\r\n" +
mApp->mProductVersion + "\r\n" +
/*"--" + aSeperator + "\r\n"
"Content-Disposition: form-data; name=\"buildnum\"\r\n" +
"\r\n" +
StrFormat("%d", mApp->mBuildNum) + "\r\n" +
"--" + aSeperator + "\r\n"
"Content-Disposition: form-data; name=\"builddate\"\r\n" +
"\r\n" +
mApp->mBuildDate + "\r\n" +
"--" + aSeperator + "\r\n"
"Content-Disposition: form-data; name=\"referid\"\r\n" +
"\r\n" +
mApp->mReferId + "\r\n" +*/
"--" + aSeperator + "\r\n"
"Content-Disposition: form-data; name=\"usertext\"\r\n" +
"\r\n" +
mUserText + "\r\n" +
"--" + aSeperator + "\r\n"
"Content-Disposition: form-data; name=\"errortitle\"\r\n" +
"\r\n" +
mErrorTitle + "\r\n" +
"--" + aSeperator + "\r\n"
"Content-Disposition: form-data; name=\"errortext\"\r\n" +
"\r\n" +
mErrorText + "\r\n";
if (mHasDemoFile)
{
Buffer aBuffer;
mApp->ReadBufferFromFile(mUploadFileName, &aBuffer, true);
aContent +=
"--" + aSeperator + "\r\n"
"Content-Disposition: form-data; name=\"demofile\"; filename=\"popcap.dmo\"\r\n" +
"Content-Type: application/octet-stream\r\n" +
"\r\n";
aContent.insert(aContent.end(), (char*) aBuffer.GetDataPtr(), (char*) aBuffer.GetDataPtr() + aBuffer.GetDataLen());
aContent += "\r\n";
}
aContent +=
"--" + aSeperator + "--\r\n";
std::string aSendString =
"POST /deluxe_error.php HTTP/1.1\r\n"
"Content-Type: multipart/form-data; boundary=" + aSeperator + "\r\n"
"User-Agent: Mozilla/4.0 (compatible; popcap)\r\n" +
"Host: " + mSubmitHost + "\r\n" +
"Content-Length: " + StrFormat("%d", aContent.length()) + "\r\n" +
"Connection: close\r\n" +
"\r\n" + aContent;
mSubmitReportTransfer.SendRequestString(mSubmitHost, aSendString);
}
void SEHCatcher::ShowSubmitInfoDialog()
{
WNDCLASSA wc;
wc.style = 0;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = ::GetSysColorBrush(COLOR_BTNFACE);
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wc.hIcon = ::LoadIcon(NULL, IDI_ERROR);
wc.hInstance = gHInstance;
wc.lpfnWndProc = SubmitInfoWindowProc;
wc.lpszClassName = "SubmitInfoWindow";
wc.lpszMenuName = NULL;
RegisterClassA(&wc);
RECT aRect;
aRect.left = 0;
aRect.top = 0;
aRect.right = 400;
aRect.bottom = 300;
DWORD aWindowStyle = WS_CLIPCHILDREN | WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
BOOL worked = AdjustWindowRect(&aRect, aWindowStyle, FALSE);
HWND aHWnd = CreateWindowA("SubmitInfoWindow", "Error Details",
aWindowStyle,
64+16, 64+16,
aRect.right - aRect.left,
aRect.bottom - aRect.top,
NULL,
NULL,
gHInstance,
0);
HWND aLabelWindow = CreateWindowW(L"EDIT", mSubmitMessage.c_str(),
WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_READONLY,
8, 8,
400-8-8,
84,
aHWnd,
NULL,
gHInstance,
0);
HDC aDC = ::GetDC(aLabelWindow);
int aFontHeight = -MulDiv(9, 96, 72);
::ReleaseDC(aLabelWindow, aDC);
HFONT aBoldArialFont = CreateFontA(aFontHeight, 0, 0, 0, FW_BOLD, 0, 0,
false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, "Arial");
if (!gUseDefaultFonts)
SendMessage(aLabelWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0);
mEditWindow = CreateWindowA("EDIT", "",
WS_VISIBLE | WS_CHILD | ES_MULTILINE | WS_BORDER | WS_VSCROLL,
8, 300-168-24-8-8,
400-8-8,
168,
aHWnd,
NULL,
gHInstance,
0);
aDC = ::GetDC(mEditWindow);
aFontHeight = -MulDiv(8, 96, 72);
::ReleaseDC(mEditWindow, aDC);
HFONT aCourierNewFont = CreateFontA(aFontHeight, 0, 0, 0, FW_NORMAL, 0, 0,
false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, "Courier New");
if (!gUseDefaultFonts)
SendMessage(mEditWindow, WM_SETFONT, (WPARAM) mDialogFont, 0);
SetFocus(mEditWindow);
int aButtonWidth = (400 - 8 - 8 - 8) / 2;
int aCurX = 8;
aWindowStyle = WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON;
if (mApp == NULL)
aWindowStyle |= WS_DISABLED;
mYesButtonWindow = CreateWindowA("BUTTON", "Continue",
aWindowStyle,
aCurX, 300-24-8,
aButtonWidth,
24,
aHWnd,
NULL,
gHInstance,
0);
if (!gUseDefaultFonts)
SendMessage(mYesButtonWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0);
aCurX += aButtonWidth+8;
mNoButtonWindow = CreateWindowA("BUTTON", "Abort",
WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON,
aCurX, 300-24-8,
aButtonWidth,
24,
aHWnd,
NULL,
gHInstance,
0);
if (!gUseDefaultFonts)
SendMessage(mNoButtonWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0);
ShowWindow(aHWnd, SW_NORMAL);
}
void SEHCatcher::ShowErrorDialog(const std::string& theErrorTitle, const std::string& theErrorText)
{
OSVERSIONINFO aVersionInfo;
aVersionInfo.dwOSVersionInfoSize = sizeof(aVersionInfo);
GetVersionEx(&aVersionInfo);
// Setting fonts on 98 causes weirdo crash things in GDI upon the second crash.
// That's no good.
gUseDefaultFonts = aVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT;
int aHeight = -MulDiv(8, 96, 72);
mDialogFont = ::CreateFontA(aHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE,
false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, "Tahoma");
aHeight = -MulDiv(10, 96, 72);
mBoldFont = ::CreateFontA(aHeight, 0, 0, 0, FW_BOLD, FALSE, FALSE,
false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, "Tahoma");
::SetCursor(::LoadCursor(NULL, IDC_ARROW));
mErrorTitle = theErrorTitle;
mErrorText = theErrorText;
WNDCLASSA wc;
wc.style = 0;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = ::GetSysColorBrush(COLOR_BTNFACE);
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wc.hIcon = ::LoadIcon(NULL, IDI_ERROR);
wc.hInstance = gHInstance;
wc.lpfnWndProc = SEHWindowProc;
wc.lpszClassName = "SEHWindow";
wc.lpszMenuName = NULL;
RegisterClassA(&wc);
RECT aRect;
aRect.left = 0;
aRect.top = 0;
aRect.right = 400;
aRect.bottom = 300;
DWORD aWindowStyle = WS_CLIPCHILDREN | WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
BOOL worked = AdjustWindowRect(&aRect, aWindowStyle, FALSE);
HWND aHWnd = CreateWindowW(L"SEHWindow", L"Fatal Error!",
aWindowStyle,
64, 64,
aRect.right - aRect.left,
aRect.bottom - aRect.top,
NULL,
NULL,
gHInstance,
0);
HWND aLabelWindow = CreateWindowW(L"EDIT",
mCrashMessage.c_str(),
WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_READONLY,
8, 8,
400-8-8,
84,
aHWnd,
NULL,
gHInstance,
0);
int aFontHeight = -MulDiv(9, 96, 72);
HFONT aBoldArialFont = CreateFontA(aFontHeight, 0, 0, 0, FW_BOLD, 0, 0,
false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, "Arial");
if (!gUseDefaultFonts)
SendMessage(aLabelWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0);
HWND anEditWindow = CreateWindowA("EDIT", theErrorText.c_str(),
WS_VISIBLE | WS_CHILD | ES_MULTILINE | WS_BORDER | WS_VSCROLL | ES_READONLY,
8, 300-168-24-8-8,
400-8-8,
168,
aHWnd,
NULL,
gHInstance,
0);
aFontHeight = -MulDiv(8, 96, 72);
HFONT aCourierNewFont = CreateFontA(aFontHeight, 0, 0, 0, FW_NORMAL, 0, 0,
false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, "Courier New");
if (!gUseDefaultFonts)
SendMessage(anEditWindow, WM_SETFONT, (WPARAM) aCourierNewFont, 0);
aWindowStyle = WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON;
if (mApp == NULL)
aWindowStyle |= WS_DISABLED;
#ifdef _DEBUG
bool doDebugButton = true;
#else
bool doDebugButton = false;
#endif
bool canSubmit = mAllowSubmit && !mSubmitHost.empty();
int aNumButtons = 1 + (doDebugButton ? 1 : 0) + (canSubmit ? 1 : 0);
int aButtonWidth = (400 - 8 - 8 - (aNumButtons - 1)*8) / aNumButtons;
int aCurX = 8;
if (canSubmit)
{
mYesButtonWindow = CreateWindowA("BUTTON", "Send Report",
aWindowStyle,
aCurX, 300-24-8,
aButtonWidth,
24,
aHWnd,
NULL,
gHInstance,
0);
if (!gUseDefaultFonts)
SendMessage(mYesButtonWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0);
aCurX += aButtonWidth + 8;
}
if (doDebugButton)
{
mDebugButtonWindow = CreateWindowA("BUTTON", "Debug",
aWindowStyle,
aCurX, 300-24-8,
aButtonWidth,
24,
aHWnd,
NULL,
gHInstance,
0);
if (!gUseDefaultFonts)
SendMessage(mDebugButtonWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0);
aCurX += aButtonWidth + 8;
}
mNoButtonWindow = CreateWindowA("BUTTON", "Close Now",
WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON,
aCurX, 300-24-8,
aButtonWidth,
24,
aHWnd,
NULL,
gHInstance,
0);
if (!gUseDefaultFonts)
SendMessage(mNoButtonWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0);
ShowWindow(aHWnd, SW_NORMAL);
MSG msg;
while ((GetMessage(&msg, NULL, 0, 0) > 0) && (!mExiting))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DestroyWindow(aHWnd);
DeleteObject(mDialogFont);
DeleteObject(mBoldFont);
DeleteObject(aBoldArialFont);
DeleteObject(aCourierNewFont);
}
std::string SEHCatcher::GetSysInfo()
{
std::string aDebugDump;
OSVERSIONINFOA aVersionInfo;
aVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionExA(&aVersionInfo);
aDebugDump += "Windows Ver: ";
if (aVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
aDebugDump += "NT ";
else
aDebugDump += "9x ";
char aVersionStr[20];
sprintf(aVersionStr, "%d.%d", aVersionInfo.dwMajorVersion, aVersionInfo.dwMinorVersion);
aDebugDump += aVersionStr;
aDebugDump += " ";
aDebugDump += aVersionInfo.szCSDVersion;
aDebugDump += " ";
sprintf(aVersionStr, "%d", aVersionInfo.dwBuildNumber);
aDebugDump += "Build ";
aDebugDump += aVersionStr;
aDebugDump += "\r\n";
HMODULE aMod;
char aPath[256];
if (mApp != NULL)
{
aMod = LoadLibraryA("ddraw.dll");
if (aMod != NULL)
{
GetModuleFileNameA(aMod, aPath, 256);
aDebugDump += "DDraw Ver: " + mApp->GetProductVersion(aPath) + "\r\n";
FreeLibrary(aMod);
}
aMod = LoadLibraryA("dsound.dll");
if (aMod != NULL)
{
GetModuleFileNameA(aMod, aPath, 256);
aDebugDump += "DSound Ver: " + mApp->GetProductVersion(aPath) + "\r\n";
FreeLibrary(aMod);
}
}
return aDebugDump;
}