Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 244 | chris | 1 | #include "SEHCatcher.h" |
| 2 | #include "SexyAppBase.h" |
||
| 3 | #include <fstream> |
||
| 4 | #include <process.h> |
||
| 5 | |||
| 6 | #ifdef ZYLOM |
||
| 7 | #include "zylomso.h" |
||
| 8 | using namespace zylom::zylomso; |
||
| 9 | #endif |
||
| 10 | |||
| 11 | using namespace Sexy; |
||
| 12 | |||
| 13 | LPTOP_LEVEL_EXCEPTION_FILTER SEHCatcher::mPreviousFilter; |
||
| 14 | SexyAppBase* SEHCatcher::mApp = NULL; |
||
| 15 | HFONT SEHCatcher::mDialogFont = NULL; |
||
| 16 | HFONT SEHCatcher::mBoldFont = NULL; |
||
| 17 | bool SEHCatcher::mHasDemoFile = false; |
||
| 18 | bool SEHCatcher::mDebugError = false; |
||
| 19 | std::string SEHCatcher::mErrorTitle; |
||
| 20 | std::string SEHCatcher::mErrorText; |
||
| 21 | std::string SEHCatcher::mUserText; |
||
| 22 | std::string SEHCatcher::mUploadFileName; |
||
| 23 | HWND SEHCatcher::mYesButtonWindow = NULL; |
||
| 24 | HWND SEHCatcher::mNoButtonWindow = NULL; |
||
| 25 | HWND SEHCatcher::mDebugButtonWindow = NULL; |
||
| 26 | HWND SEHCatcher::mEditWindow = NULL; |
||
| 27 | HMODULE SEHCatcher::mImageHelpLib = NULL; |
||
| 28 | SYMINITIALIZEPROC SEHCatcher::mSymInitialize = NULL; |
||
| 29 | SYMSETOPTIONSPROC SEHCatcher::mSymSetOptions = NULL; |
||
| 30 | UNDECORATESYMBOLNAMEPROC SEHCatcher::mUnDecorateSymbolName = NULL; |
||
| 31 | SYMCLEANUPPROC SEHCatcher::mSymCleanup = NULL; |
||
| 32 | STACKWALKPROC SEHCatcher::mStackWalk = NULL; |
||
| 33 | SYMFUNCTIONTABLEACCESSPROC SEHCatcher::mSymFunctionTableAccess = NULL; |
||
| 34 | SYMGETMODULEBASEPROC SEHCatcher::mSymGetModuleBase = NULL; |
||
| 35 | SYMGETSYMFROMADDRPROC SEHCatcher::mSymGetSymFromAddr = NULL; |
||
| 36 | HTTPTransfer SEHCatcher::mSubmitReportTransfer; |
||
| 37 | bool SEHCatcher::mExiting = false; |
||
| 38 | bool SEHCatcher::mShowUI = true; |
||
| 39 | bool SEHCatcher::mAllowSubmit = true; |
||
| 40 | |||
| 41 | std::wstring SEHCatcher::mCrashMessage = L"An unexpected error has occured!"; |
||
| 42 | std::string SEHCatcher::mSubmitHost; |
||
| 43 | std::wstring SEHCatcher::mSubmitMessage = L"Please help out by providing as much information as you can about this crash."; |
||
| 44 | std::wstring SEHCatcher::mSubmitErrorMessage = L"Failed to connect to server."; |
||
| 45 | |||
| 46 | static bool gUseDefaultFonts = true; |
||
| 47 | |||
| 48 | struct |
||
| 49 | { |
||
| 50 | DWORD dwExceptionCode; |
||
| 51 | char *szMessage; |
||
| 52 | } gMsgTable[] = { |
||
| 53 | { STATUS_SEGMENT_NOTIFICATION, "Segment Notification" }, |
||
| 54 | { STATUS_BREAKPOINT, "Breakpoint" }, |
||
| 55 | { STATUS_SINGLE_STEP, "Single step" }, |
||
| 56 | { STATUS_WAIT_0, "Wait 0" }, |
||
| 57 | { STATUS_ABANDONED_WAIT_0, "Abandoned Wait 0" }, |
||
| 58 | { STATUS_USER_APC, "User APC" }, |
||
| 59 | { STATUS_TIMEOUT, "Timeout" }, |
||
| 60 | { STATUS_PENDING, "Pending" }, |
||
| 61 | { STATUS_GUARD_PAGE_VIOLATION, "Guard Page Violation" }, |
||
| 62 | { STATUS_DATATYPE_MISALIGNMENT, "Data Type Misalignment" }, |
||
| 63 | { STATUS_ACCESS_VIOLATION, "Access Violation" }, |
||
| 64 | { STATUS_IN_PAGE_ERROR, "In Page Error" }, |
||
| 65 | { STATUS_NO_MEMORY, "No Memory" }, |
||
| 66 | { STATUS_ILLEGAL_INSTRUCTION, "Illegal Instruction" }, |
||
| 67 | { STATUS_NONCONTINUABLE_EXCEPTION, "Noncontinuable Exception" }, |
||
| 68 | { STATUS_INVALID_DISPOSITION, "Invalid Disposition" }, |
||
| 69 | { STATUS_ARRAY_BOUNDS_EXCEEDED, "Array Bounds Exceeded" }, |
||
| 70 | { STATUS_FLOAT_DENORMAL_OPERAND, "Float Denormal Operand" }, |
||
| 71 | { STATUS_FLOAT_DIVIDE_BY_ZERO, "Divide by Zero" }, |
||
| 72 | { STATUS_FLOAT_INEXACT_RESULT, "Float Inexact Result" }, |
||
| 73 | { STATUS_FLOAT_INVALID_OPERATION, "Float Invalid Operation" }, |
||
| 74 | { STATUS_FLOAT_OVERFLOW, "Float Overflow" }, |
||
| 75 | { STATUS_FLOAT_STACK_CHECK, "Float Stack Check" }, |
||
| 76 | { STATUS_FLOAT_UNDERFLOW, "Float Underflow" }, |
||
| 77 | { STATUS_INTEGER_DIVIDE_BY_ZERO, "Integer Divide by Zero" }, |
||
| 78 | { STATUS_INTEGER_OVERFLOW, "Integer Overflow" }, |
||
| 79 | { STATUS_PRIVILEGED_INSTRUCTION, "Privileged Instruction" }, |
||
| 80 | { STATUS_STACK_OVERFLOW, "Stack Overflow" }, |
||
| 81 | { STATUS_CONTROL_C_EXIT, "Ctrl+C Exit" }, |
||
| 82 | { 0xFFFFFFFF, "" } |
||
| 83 | }; |
||
| 84 | |||
| 85 | |||
| 86 | SEHCatcher::SEHCatcher() |
||
| 87 | { |
||
| 88 | mPreviousFilter = SetUnhandledExceptionFilter(UnhandledExceptionFilter); |
||
| 89 | } |
||
| 90 | |||
| 91 | SEHCatcher::~SEHCatcher() |
||
| 92 | { |
||
| 93 | SetUnhandledExceptionFilter(mPreviousFilter); |
||
| 94 | } |
||
| 95 | |||
| 96 | long __stdcall SEHCatcher::UnhandledExceptionFilter(LPEXCEPTION_POINTERS lpExceptPtr) |
||
| 97 | { |
||
| 98 | if (mApp != NULL) |
||
| 99 | mApp->SEHOccured(); |
||
| 100 | |||
| 101 | DoHandleDebugEvent(lpExceptPtr); |
||
| 102 | |||
| 103 | if (!mDebugError) |
||
| 104 | SetErrorMode(SEM_NOGPFAULTERRORBOX); |
||
| 105 | |||
| 106 | return EXCEPTION_CONTINUE_SEARCH; |
||
| 107 | } |
||
| 108 | |||
| 109 | bool SEHCatcher::LoadImageHelp() |
||
| 110 | { |
||
| 111 | mImageHelpLib = LoadLibraryA("IMAGEHLP.DLL"); |
||
| 112 | if (!mImageHelpLib) |
||
| 113 | return false; |
||
| 114 | |||
| 115 | mSymInitialize = (SYMINITIALIZEPROC) GetProcAddress(mImageHelpLib, "SymInitialize"); |
||
| 116 | if (!mSymInitialize) |
||
| 117 | return false; |
||
| 118 | |||
| 119 | mSymSetOptions = (SYMSETOPTIONSPROC) GetProcAddress(mImageHelpLib, "SymSetOptions"); |
||
| 120 | if (!mSymSetOptions) |
||
| 121 | return false; |
||
| 122 | |||
| 123 | mSymCleanup = (SYMCLEANUPPROC) GetProcAddress(mImageHelpLib, "SymCleanup"); |
||
| 124 | if (!mSymCleanup) |
||
| 125 | return false; |
||
| 126 | |||
| 127 | mUnDecorateSymbolName = (UNDECORATESYMBOLNAMEPROC) GetProcAddress(mImageHelpLib, "UnDecorateSymbolName"); |
||
| 128 | if (!mUnDecorateSymbolName) |
||
| 129 | return false; |
||
| 130 | |||
| 131 | mStackWalk = (STACKWALKPROC) GetProcAddress(mImageHelpLib, "StackWalk"); |
||
| 132 | if (!mStackWalk) |
||
| 133 | return false; |
||
| 134 | |||
| 135 | mSymFunctionTableAccess = (SYMFUNCTIONTABLEACCESSPROC) GetProcAddress(mImageHelpLib, "SymFunctionTableAccess"); |
||
| 136 | if (!mSymFunctionTableAccess) |
||
| 137 | return false; |
||
| 138 | |||
| 139 | mSymGetModuleBase = (SYMGETMODULEBASEPROC) GetProcAddress(mImageHelpLib, "SymGetModuleBase"); |
||
| 140 | if (!mSymGetModuleBase) |
||
| 141 | return false; |
||
| 142 | |||
| 143 | mSymGetSymFromAddr = (SYMGETSYMFROMADDRPROC) GetProcAddress(mImageHelpLib, "SymGetSymFromAddr" ); |
||
| 144 | if (!mSymGetSymFromAddr) |
||
| 145 | return false; |
||
| 146 | |||
| 147 | mSymSetOptions(SYMOPT_DEFERRED_LOADS); |
||
| 148 | |||
| 149 | // Get image filename of the main executable |
||
| 150 | char filepath[MAX_PATH], *lastdir, *pPath; |
||
| 151 | DWORD filepathlen = GetModuleFileNameA ( NULL, filepath, sizeof(filepath)); |
||
| 152 | |||
| 153 | lastdir = strrchr (filepath, '/'); |
||
| 154 | if (lastdir == NULL) lastdir = strrchr (filepath, '\\'); |
||
| 155 | if (lastdir != NULL) lastdir[0] = '\0'; |
||
| 156 | |||
| 157 | // Initialize the symbol table routines, supplying a pointer to the path |
||
| 158 | pPath = filepath; |
||
| 159 | if (strlen (filepath) == 0) pPath = NULL; |
||
| 160 | |||
| 161 | if (!mSymInitialize (GetCurrentProcess(), pPath, TRUE)) |
||
| 162 | return false; |
||
| 163 | |||
| 164 | return true; |
||
| 165 | } |
||
| 166 | |||
| 167 | void SEHCatcher::UnloadImageHelp() |
||
| 168 | { |
||
| 169 | if (mImageHelpLib != NULL) |
||
| 170 | FreeLibrary(mImageHelpLib); |
||
| 171 | } |
||
| 172 | |||
| 173 | static bool StrToLongHex(const std::string& aString, DWORD* theValue) |
||
| 174 | { |
||
| 175 | *theValue = 0; |
||
| 176 | |||
| 177 | for (int i = 0; i < (int)aString.length(); i++) |
||
| 178 | { |
||
| 179 | char aChar = aString[i]; |
||
| 180 | |||
| 181 | int aValue; |
||
| 182 | if ((aChar >= '0') && (aChar <= '9')) |
||
| 183 | aValue = aChar - '0'; |
||
| 184 | else if ((aChar >= 'A') && (aChar <= 'F')) |
||
| 185 | aValue = (aChar - 'A') + 10; |
||
| 186 | else if ((aChar >= 'a') && (aChar <= 'f')) |
||
| 187 | aValue = (aChar - 'a') + 10; |
||
| 188 | else |
||
| 189 | return false; |
||
| 190 | |||
| 191 | *theValue += aValue << ((aString.length()-i-1)*4); |
||
| 192 | } |
||
| 193 | |||
| 194 | return true; |
||
| 195 | } |
||
| 196 | |||
| 197 | void SEHCatcher::GetSymbolsFromMapFile(std::string &theDebugDump) |
||
| 198 | { |
||
| 199 | DWORD aTick = GetTickCount(); |
||
| 200 | WIN32_FIND_DATAA aFindData; |
||
| 201 | HANDLE aFindHandle = FindFirstFileA("*.map",&aFindData); |
||
| 202 | if (aFindHandle==INVALID_HANDLE_VALUE) |
||
| 203 | return; |
||
| 204 | |||
| 205 | FindClose(aFindHandle); |
||
| 206 | |||
| 207 | typedef std::pair<DWORD,DWORD> SymbolPair; |
||
| 208 | typedef std::map<SymbolPair,std::string> SymbolMap; |
||
| 209 | typedef std::pair<std::string,int> LineNumPair; |
||
| 210 | typedef std::map<SymbolPair,LineNumPair> LineNumMap; |
||
| 211 | |||
| 212 | /**/ |
||
| 213 | SymbolMap aSymbolMap; |
||
| 214 | LineNumMap aLineNumMap; |
||
| 215 | std::fstream aFile(aFindData.cFileName, std::ios::in); |
||
| 216 | |||
| 217 | if (!aFile.is_open()) |
||
| 218 | return; |
||
| 219 | |||
| 220 | // Parse map file |
||
| 221 | std::string aCurLinesFile; |
||
| 222 | while (!aFile.eof()) |
||
| 223 | { |
||
| 224 | char aStr[4096]; |
||
| 225 | aFile.getline(aStr, 4096); |
||
| 226 | |||
| 227 | std::string aString = aStr; |
||
| 228 | |||
| 229 | if ((aString.length() > 22) && (aString[5] == ':')) |
||
| 230 | { |
||
| 231 | std::string aFoundPreStr = aString.substr(1, 4); |
||
| 232 | std::string aFoundPostStr = aString.substr(6, 8); |
||
| 233 | |||
| 234 | DWORD aFoundPreVal; |
||
| 235 | DWORD aFoundPostVal; |
||
| 236 | |||
| 237 | if (StrToLongHex(aFoundPreStr, &aFoundPreVal) && StrToLongHex(aFoundPostStr, &aFoundPostVal)) |
||
| 238 | { |
||
| 239 | int aSpacePos = aString.find(' ', 21); |
||
| 240 | |||
| 241 | if ((aString[20] == ' ') && (aSpacePos >= 0)) |
||
| 242 | aSymbolMap[SymbolPair(aFoundPreVal,aFoundPostVal)] = aString.substr(21, aSpacePos-21); |
||
| 243 | } |
||
| 244 | } |
||
| 245 | else if (strcmp(aString.substr(0, strlen("Line numbers")).c_str(), "Line numbers") == 0) |
||
| 246 | { |
||
| 247 | int aSegmentPos = aString.rfind(')'); |
||
| 248 | if (aSegmentPos == -1) |
||
| 249 | aSegmentPos = aString.length(); |
||
| 250 | |||
| 251 | int aPreLen = strlen("Line numbers for "); |
||
| 252 | |||
| 253 | int aStartPos = aString.find('('); |
||
| 254 | |||
| 255 | aCurLinesFile = aString.substr(aStartPos + 1, aSegmentPos - aStartPos - 1); |
||
| 256 | } |
||
| 257 | else if ((aCurLinesFile.length() > 0) && (aString.length() == 80)) |
||
| 258 | { |
||
| 259 | // Line number stuff |
||
| 260 | |||
| 261 | for (int i = 0; i < 4; i++) |
||
| 262 | { |
||
| 263 | int aStartLoc = 20 * i; |
||
| 264 | |||
| 265 | int aLine = atoi(aString.substr(aStartLoc, 6).c_str()); |
||
| 266 | |||
| 267 | std::string aFoundPreStr = aString.substr(aStartLoc + 7, 4); |
||
| 268 | std::string aFoundPostStr = aString.substr(aStartLoc + 12, 8); |
||
| 269 | |||
| 270 | DWORD aFoundPreVal; |
||
| 271 | DWORD aFoundPostVal; |
||
| 272 | |||
| 273 | if (StrToLongHex(aFoundPreStr, &aFoundPreVal) && StrToLongHex(aFoundPostStr, &aFoundPostVal)) |
||
| 274 | { |
||
| 275 | aLineNumMap[SymbolPair(aFoundPreVal,aFoundPostVal)] = LineNumPair(aCurLinesFile,aLine); |
||
| 276 | } |
||
| 277 | } |
||
| 278 | } |
||
| 279 | } |
||
| 280 | |||
| 281 | // Parse stack trace |
||
| 282 | for (int i = 0; i < (int)theDebugDump.length(); i++) |
||
| 283 | { |
||
| 284 | if (theDebugDump.at(i) == ':') |
||
| 285 | { |
||
| 286 | std::string aFindPreStr = theDebugDump.substr(i-4, 4); |
||
| 287 | std::string aFindPostStr = theDebugDump.substr(i+1, 8); |
||
| 288 | |||
| 289 | DWORD aFindPreVal; |
||
| 290 | DWORD aFindPostVal; |
||
| 291 | |||
| 292 | if (StrToLongHex(aFindPreStr, &aFindPreVal) && StrToLongHex(aFindPostStr, &aFindPostVal)) |
||
| 293 | { |
||
| 294 | |||
| 295 | int aBestDist = -1; |
||
| 296 | SymbolMap::iterator aSymbolItr = aSymbolMap.lower_bound(SymbolPair(aFindPreVal,aFindPostVal)); |
||
| 297 | if (aSymbolItr!=aSymbolMap.end() && aSymbolItr!=aSymbolMap.begin() && aSymbolItr->first.second!=aFindPostVal) |
||
| 298 | --aSymbolItr; |
||
| 299 | |||
| 300 | if (aSymbolItr!=aSymbolMap.end() && aSymbolItr->first.first==aFindPreVal) |
||
| 301 | aBestDist = aFindPostVal - aSymbolItr->first.second; |
||
| 302 | |||
| 303 | if (aBestDist != -1) |
||
| 304 | { |
||
| 305 | std::string &aBestName = aSymbolItr->second; |
||
| 306 | |||
| 307 | char aSymbolName[4096]; |
||
| 308 | |||
| 309 | if (mUnDecorateSymbolName(aBestName.c_str(), aSymbolName, 4096, |
||
| 310 | UNDNAME_NO_ALLOCATION_MODEL | UNDNAME_NO_ACCESS_SPECIFIERS | |
||
| 311 | UNDNAME_NO_THROW_SIGNATURES | UNDNAME_NO_MEMBER_TYPE) == 0) |
||
| 312 | strcpy(aSymbolName, aBestName.c_str()); |
||
| 313 | |||
| 314 | if (aBestDist != 0) |
||
| 315 | { |
||
| 316 | sprintf(aSymbolName+strlen(aSymbolName), "+0x%X", aBestDist); |
||
| 317 | } |
||
| 318 | |||
| 319 | std::string aNewText = aSymbolName; |
||
| 320 | |||
| 321 | LineNumMap::iterator aLineNumItr = aLineNumMap.lower_bound(SymbolPair(aFindPreVal,aFindPostVal)); |
||
| 322 | if (aLineNumItr!=aLineNumMap.end() && aLineNumItr!=aLineNumMap.begin() && aLineNumItr->first.second!=aFindPostVal) |
||
| 323 | --aLineNumItr; |
||
| 324 | |||
| 325 | if (aLineNumItr!=aLineNumMap.end() && aLineNumItr->first.first==aFindPreVal) |
||
| 326 | { |
||
| 327 | std::string &aBestFile = aLineNumItr->second.first; |
||
| 328 | int aBestLine = aLineNumItr->second.second; |
||
| 329 | int aBestLineDist = aFindPostVal - aLineNumItr->first.second; |
||
| 330 | |||
| 331 | char aDistStr[4096]; |
||
| 332 | sprintf(aDistStr, "\r\n %s line %d +0x%X", aBestFile.c_str(), aBestLine, aBestLineDist); |
||
| 333 | aNewText += aDistStr; |
||
| 334 | } |
||
| 335 | |||
| 336 | theDebugDump.erase(i-4, 13); |
||
| 337 | theDebugDump.insert(i-4, aNewText.c_str()); |
||
| 338 | } |
||
| 339 | } |
||
| 340 | } |
||
| 341 | } |
||
| 342 | |||
| 343 | // MessageBox(NULL,StrFormat("%d",GetTickCount()-aTick).c_str(),"Time",MB_OK); |
||
| 344 | } |
||
| 345 | |||
| 346 | void SEHCatcher::DoHandleDebugEvent(LPEXCEPTION_POINTERS lpEP) |
||
| 347 | { |
||
| 348 | bool hasImageHelp = LoadImageHelp(); |
||
| 349 | |||
| 350 | std::string anErrorTitle; |
||
| 351 | std::string aDebugDump; |
||
| 352 | |||
| 353 | char aBuffer[2048]; |
||
| 354 | |||
| 355 | /////////////////////////// |
||
| 356 | // first name the exception |
||
| 357 | char *szName = NULL; |
||
| 358 | for (int i=0; gMsgTable[i].dwExceptionCode != 0xFFFFFFFF; i++) |
||
| 359 | { |
||
| 360 | if (gMsgTable[i].dwExceptionCode == lpEP->ExceptionRecord->ExceptionCode) |
||
| 361 | { |
||
| 362 | szName = gMsgTable[i].szMessage; |
||
| 363 | break; |
||
| 364 | } |
||
| 365 | } |
||
| 366 | |||
| 367 | if (szName != NULL) |
||
| 368 | { |
||
| 369 | sprintf(aBuffer,"Exception: %s (code 0x%x) at address %08X in thread %X\r\n", |
||
| 370 | szName, lpEP->ExceptionRecord->ExceptionCode, |
||
| 371 | lpEP->ExceptionRecord->ExceptionAddress, GetCurrentThreadId()); |
||
| 372 | } |
||
| 373 | else |
||
| 374 | { |
||
| 375 | sprintf(aBuffer,"Unknown exception: (code 0x%x) at address %08X in thread %X\r\n", |
||
| 376 | lpEP->ExceptionRecord->ExceptionCode, |
||
| 377 | lpEP->ExceptionRecord->ExceptionAddress, GetCurrentThreadId()); |
||
| 378 | } |
||
| 379 | |||
| 380 | aDebugDump += aBuffer; |
||
| 381 | |||
| 382 | /////////////////////////////////////////////////////////// |
||
| 383 | // Get logical address of the module where exception occurs |
||
| 384 | DWORD section, offset; |
||
| 385 | GetLogicalAddress(lpEP->ExceptionRecord->ExceptionAddress, aBuffer, sizeof(aBuffer), section, offset); |
||
| 386 | aDebugDump += "Module: " + GetFilename(aBuffer) + "\r\n"; |
||
| 387 | sprintf(aBuffer, "Logical Address: %04X:%08X\r\n", section, offset); |
||
| 388 | aDebugDump += aBuffer; |
||
| 389 | |||
| 390 | aDebugDump += "\r\n"; |
||
| 391 | |||
| 392 | anErrorTitle = StrFormat("Exception at %04X:%08X", section, offset); |
||
| 393 | |||
| 394 | std::string aWalkString; |
||
| 395 | |||
| 396 | if (hasImageHelp) |
||
| 397 | aWalkString = ImageHelpWalk(lpEP->ContextRecord, 0); |
||
| 398 | |||
| 399 | if (aWalkString.length() == 0) |
||
| 400 | aWalkString = IntelWalk(lpEP->ContextRecord, 0); |
||
| 401 | |||
| 402 | aDebugDump += aWalkString; |
||
| 403 | |||
| 404 | aDebugDump += "\r\n"; |
||
| 405 | sprintf(aBuffer, ("EAX:%08X EBX:%08X ECX:%08X EDX:%08X ESI:%08X EDI:%08X\r\n"), |
||
| 406 | lpEP->ContextRecord->Eax, lpEP->ContextRecord->Ebx, lpEP->ContextRecord->Ecx, lpEP->ContextRecord->Edx, lpEP->ContextRecord->Esi, lpEP->ContextRecord->Edi); |
||
| 407 | aDebugDump += aBuffer; |
||
| 408 | sprintf(aBuffer, "EIP:%08X ESP:%08X EBP:%08X\r\n", lpEP->ContextRecord->Eip, lpEP->ContextRecord->Esp, lpEP->ContextRecord->Ebp); |
||
| 409 | aDebugDump += aBuffer; |
||
| 410 | 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 ); |
||
| 411 | aDebugDump += aBuffer; |
||
| 412 | sprintf(aBuffer, "Flags:%08X\r\n", lpEP->ContextRecord->EFlags ); |
||
| 413 | aDebugDump += aBuffer; |
||
| 414 | |||
| 415 | aDebugDump += "\r\n"; |
||
| 416 | aDebugDump += GetSysInfo(); |
||
| 417 | |||
| 418 | if (mApp != NULL) |
||
| 419 | { |
||
| 420 | std::string aGameSEHInfo = mApp->GetGameSEHInfo(); |
||
| 421 | if (aGameSEHInfo.length() > 0) |
||
| 422 | { |
||
| 423 | aDebugDump += "\r\n"; |
||
| 424 | aDebugDump += aGameSEHInfo; |
||
| 425 | } |
||
| 426 | |||
| 427 | mApp->CopyToClipboard(aDebugDump); |
||
| 428 | } |
||
| 429 | |||
| 430 | if (hasImageHelp) |
||
| 431 | GetSymbolsFromMapFile(aDebugDump); |
||
| 432 | |||
| 433 | WriteToFile(aDebugDump); |
||
| 434 | |||
| 435 | #ifdef ZYLOM |
||
| 436 | ZylomGS_StandAlone_SendBugReport((char*) aDebugDump.c_str()); |
||
| 437 | #else |
||
| 438 | if (mApp != NULL) |
||
| 439 | { |
||
| 440 | if (mApp->mRecordingDemoBuffer) |
||
| 441 | { |
||
| 442 | // Make sure we have enough update block things in there to |
||
| 443 | // get to the final crashing update |
||
| 444 | mApp->WriteDemoTimingBlock(); |
||
| 445 | mApp->mDemoBuffer.WriteNumBits(0, 1); |
||
| 446 | mApp->mDemoBuffer.WriteNumBits(DEMO_IDLE, 5); |
||
| 447 | mApp->WriteDemoBuffer(); |
||
| 448 | mUploadFileName = mApp->mDemoFileName; |
||
| 449 | } |
||
| 450 | |||
| 451 | mHasDemoFile = mApp->mRecordingDemoBuffer; |
||
| 452 | std::string anUploadFile = mApp->NotifyCrashHook(); |
||
| 453 | if (!anUploadFile.empty()) |
||
| 454 | { |
||
| 455 | mUploadFileName = anUploadFile; |
||
| 456 | mHasDemoFile = true; |
||
| 457 | } |
||
| 458 | |||
| 459 | mApp->mRecordingDemoBuffer = false; |
||
| 460 | mApp->mPlayingDemoBuffer = false; |
||
| 461 | } |
||
| 462 | |||
| 463 | if (mShowUI) |
||
| 464 | ShowErrorDialog(anErrorTitle, aDebugDump); |
||
| 465 | #endif |
||
| 466 | |||
| 467 | //::MessageBox(NULL, aDebugDump.c_str(), "ERROR", MB_ICONERROR); |
||
| 468 | |||
| 469 | UnloadImageHelp(); |
||
| 470 | } |
||
| 471 | |||
| 472 | std::string SEHCatcher::IntelWalk(PCONTEXT theContext, int theSkipCount) |
||
| 473 | { |
||
| 474 | std::string aDebugDump; |
||
| 475 | char aBuffer[2048]; |
||
| 476 | |||
| 477 | DWORD pc = theContext->Eip; |
||
| 478 | PDWORD pFrame, pPrevFrame; |
||
| 479 | |||
| 480 | pFrame = (PDWORD)theContext->Ebp; |
||
| 481 | |||
| 482 | for (;;) |
||
| 483 | { |
||
| 484 | char szModule[MAX_PATH] = ""; |
||
| 485 | DWORD section = 0, offset = 0; |
||
| 486 | |||
| 487 | GetLogicalAddress((PVOID)pc, szModule, sizeof(szModule), section, offset); |
||
| 488 | |||
| 489 | sprintf(aBuffer, "%08X %08X %04X:%08X %s\r\n", |
||
| 490 | pc, pFrame, section, offset, GetFilename(szModule).c_str()); |
||
| 491 | aDebugDump += aBuffer; |
||
| 492 | |||
| 493 | pc = pFrame[1]; |
||
| 494 | |||
| 495 | pPrevFrame = pFrame; |
||
| 496 | |||
| 497 | pFrame = (PDWORD)pFrame[0]; // proceed to next higher frame on stack |
||
| 498 | |||
| 499 | if ((DWORD)pFrame & 3) // Frame pointer must be aligned on a |
||
| 500 | break; // DWORD boundary. Bail if not so. |
||
| 501 | |||
| 502 | if (pFrame <= pPrevFrame) |
||
| 503 | break; |
||
| 504 | |||
| 505 | // Can two DWORDs be read from the supposed frame address? |
||
| 506 | if (IsBadWritePtr(pFrame, sizeof(PVOID)*2)) |
||
| 507 | break; |
||
| 508 | }; |
||
| 509 | |||
| 510 | return aDebugDump; |
||
| 511 | } |
||
| 512 | |||
| 513 | std::string SEHCatcher::ImageHelpWalk(PCONTEXT theContext, int theSkipCount) |
||
| 514 | { |
||
| 515 | char aBuffer[2048]; |
||
| 516 | std::string aDebugDump; |
||
| 517 | |||
| 518 | STACKFRAME sf; |
||
| 519 | memset( &sf, 0, sizeof(sf) ); |
||
| 520 | |||
| 521 | // Initialize the STACKFRAME structure for the first call. This is only |
||
| 522 | // necessary for Intel CPUs, and isn't mentioned in the documentation. |
||
| 523 | sf.AddrPC.Offset = theContext->Eip; |
||
| 524 | sf.AddrPC.Mode = AddrModeFlat; |
||
| 525 | sf.AddrStack.Offset = theContext->Esp; |
||
| 526 | sf.AddrStack.Mode = AddrModeFlat; |
||
| 527 | sf.AddrFrame.Offset = theContext->Ebp; |
||
| 528 | sf.AddrFrame.Mode = AddrModeFlat; |
||
| 529 | |||
| 530 | int aLevelCount = 0; |
||
| 531 | |||
| 532 | for (;;) |
||
| 533 | { |
||
| 534 | if (!mStackWalk(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(), |
||
| 535 | &sf, NULL /*theContext*/, NULL, mSymFunctionTableAccess, mSymGetModuleBase, 0)) |
||
| 536 | { |
||
| 537 | DWORD lastErr = GetLastError(); |
||
| 538 | sprintf(aBuffer, "StackWalk failed (error %d)\r\n", lastErr); |
||
| 539 | aDebugDump += aBuffer; |
||
| 540 | break; |
||
| 541 | } |
||
| 542 | |||
| 543 | if ((sf.AddrFrame.Offset == 0) || (sf.AddrPC.Offset == 0)) |
||
| 544 | break; |
||
| 545 | |||
| 546 | if (theSkipCount > 0) |
||
| 547 | { |
||
| 548 | theSkipCount--; |
||
| 549 | continue; |
||
| 550 | } |
||
| 551 | |||
| 552 | BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 512]; |
||
| 553 | PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)symbolBuffer; |
||
| 554 | pSymbol->SizeOfStruct = sizeof(symbolBuffer); |
||
| 555 | pSymbol->MaxNameLength = 512; |
||
| 556 | |||
| 557 | DWORD symDisplacement = 0; // Displacement of the input address, |
||
| 558 | // relative to the start of the symbol |
||
| 559 | |||
| 560 | if (mSymGetSymFromAddr(GetCurrentProcess(), sf.AddrPC.Offset, &symDisplacement, pSymbol)) |
||
| 561 | { |
||
| 562 | char aUDName[256]; |
||
| 563 | mUnDecorateSymbolName(pSymbol->Name, aUDName, 256, |
||
| 564 | UNDNAME_NO_ALLOCATION_MODEL | UNDNAME_NO_ALLOCATION_LANGUAGE | |
||
| 565 | UNDNAME_NO_MS_THISTYPE | UNDNAME_NO_ACCESS_SPECIFIERS | |
||
| 566 | UNDNAME_NO_THISTYPE | UNDNAME_NO_MEMBER_TYPE | |
||
| 567 | UNDNAME_NO_RETURN_UDT_MODEL | UNDNAME_NO_THROW_SIGNATURES | |
||
| 568 | UNDNAME_NO_SPECIAL_SYMS); |
||
| 569 | |||
| 570 | sprintf(aBuffer, "%08X %08X %hs+%X\r\n", |
||
| 571 | sf.AddrFrame.Offset, sf.AddrPC.Offset, aUDName, symDisplacement); |
||
| 572 | } |
||
| 573 | else // No symbol found. Print out the logical address instead. |
||
| 574 | { |
||
| 575 | char szModule[MAX_PATH]; |
||
| 576 | DWORD section = 0, offset = 0; |
||
| 577 | |||
| 578 | GetLogicalAddress((PVOID)sf.AddrPC.Offset, szModule, sizeof(szModule), section, offset); |
||
| 579 | sprintf(aBuffer, "%08X %08X %04X:%08X %s\r\n", sf.AddrFrame.Offset, sf.AddrPC.Offset, section, offset, GetFilename(szModule).c_str()); |
||
| 580 | } |
||
| 581 | aDebugDump += aBuffer; |
||
| 582 | |||
| 583 | sprintf(aBuffer, "Params: %08X %08X %08X %08X\r\n", sf.Params[0], sf.Params[1], sf.Params[2], sf.Params[3]); |
||
| 584 | aDebugDump += aBuffer; |
||
| 585 | aDebugDump += "\r\n"; |
||
| 586 | |||
| 587 | aLevelCount++; |
||
| 588 | } |
||
| 589 | |||
| 590 | return aDebugDump; |
||
| 591 | } |
||
| 592 | |||
| 593 | bool SEHCatcher::GetLogicalAddress(void* addr, char* szModule, DWORD len, DWORD& section, DWORD& offset) |
||
| 594 | { |
||
| 595 | MEMORY_BASIC_INFORMATION mbi; |
||
| 596 | |||
| 597 | if (!VirtualQuery(addr, &mbi, sizeof(mbi))) |
||
| 598 | return false; |
||
| 599 | |||
| 600 | DWORD hMod = (DWORD)mbi.AllocationBase; |
||
| 601 | |||
| 602 | if (!GetModuleFileNameA((HMODULE) hMod, szModule, len)) |
||
| 603 | return false; |
||
| 604 | |||
| 605 | // Point to the DOS header in memory |
||
| 606 | PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER) hMod; |
||
| 607 | |||
| 608 | // From the DOS header, find the NT (PE) header |
||
| 609 | PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS) (hMod + pDosHdr->e_lfanew); |
||
| 610 | |||
| 611 | PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHdr); |
||
| 612 | |||
| 613 | DWORD rva = (DWORD) addr - hMod; // RVA is offset from module load address |
||
| 614 | |||
| 615 | // Iterate through the section table, looking for the one that encompasses |
||
| 616 | // the linear address. |
||
| 617 | for (unsigned i = 0; i < pNtHdr->FileHeader.NumberOfSections; i++, pSection++) |
||
| 618 | { |
||
| 619 | DWORD sectionStart = pSection->VirtualAddress; |
||
| 620 | DWORD sectionEnd = sectionStart + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize); |
||
| 621 | |||
| 622 | // Is the address in this section??? |
||
| 623 | if ((rva >= sectionStart) && (rva <= sectionEnd)) |
||
| 624 | { |
||
| 625 | // Yes, address is in the section. Calculate section and offset, |
||
| 626 | // and store in the "section" & "offset" params, which were |
||
| 627 | // passed by reference. |
||
| 628 | section = i+1; |
||
| 629 | offset = rva - sectionStart; |
||
| 630 | return true; |
||
| 631 | } |
||
| 632 | } |
||
| 633 | |||
| 634 | return false; // Should never get here! |
||
| 635 | } |
||
| 636 | |||
| 637 | std::string SEHCatcher::GetFilename(const std::string& thePath) |
||
| 638 | { |
||
| 639 | int aLastSlash = max((int) thePath.rfind('\\'), (int) thePath.rfind('/')); |
||
| 640 | |||
| 641 | if (aLastSlash >= 0) |
||
| 642 | { |
||
| 643 | return thePath.substr(aLastSlash+1); |
||
| 644 | } |
||
| 645 | else |
||
| 646 | return thePath; |
||
| 647 | } |
||
| 648 | |||
| 649 | HWND gEditWindow = NULL; |
||
| 650 | int aCount = 0; |
||
| 651 | |||
| 652 | static LRESULT CALLBACK SEHProgressWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) |
||
| 653 | { |
||
| 654 | switch (uMsg) |
||
| 655 | { |
||
| 656 | case WM_COMMAND: |
||
| 657 | { |
||
| 658 | HWND hwndCtl = (HWND) lParam; |
||
| 659 | if (hwndCtl == SEHCatcher::mNoButtonWindow) |
||
| 660 | { |
||
| 661 | // Cancel it, man! |
||
| 662 | SEHCatcher::mSubmitReportTransfer.Abort(); |
||
| 663 | } |
||
| 664 | } |
||
| 665 | break; |
||
| 666 | case WM_CLOSE: |
||
| 667 | SEHCatcher::mSubmitReportTransfer.Abort(); |
||
| 668 | return 0; |
||
| 669 | case WM_TIMER: |
||
| 670 | { |
||
| 671 | if (wParam == 0) |
||
| 672 | { |
||
| 673 | int aResult = SEHCatcher::mSubmitReportTransfer.GetResultCode(); |
||
| 674 | |||
| 675 | if ((aResult != HTTPTransfer::RESULT_NOT_COMPLETED) && |
||
| 676 | (aResult != HTTPTransfer::RESULT_NOT_STARTED)) |
||
| 677 | { |
||
| 678 | if ((aResult != HTTPTransfer::RESULT_DONE) && |
||
| 679 | (aResult != HTTPTransfer::RESULT_ABORTED)) |
||
| 680 | { |
||
| 681 | SEHCatcher::mSubmitReportTransfer.Reset(); |
||
| 682 | |||
| 683 | if (::MessageBoxW(hWnd, SEHCatcher::mSubmitErrorMessage.c_str(), |
||
| 684 | L"Connection Failed", MB_RETRYCANCEL | MB_APPLMODAL) == IDRETRY) |
||
| 685 | { |
||
| 686 | _beginthread(SEHCatcher::SubmitReportThread, 0, 0); |
||
| 687 | return 0; |
||
| 688 | } |
||
| 689 | } |
||
| 690 | |||
| 691 | SEHCatcher::mExiting = true; |
||
| 692 | } |
||
| 693 | } |
||
| 694 | else if (wParam == 1) |
||
| 695 | { |
||
| 696 | ++aCount; |
||
| 697 | |||
| 698 | std::string aNewString = "Please Wait"; |
||
| 699 | |||
| 700 | for (int i = 0; i < (aCount % 10); i++) |
||
| 701 | aNewString += " ."; |
||
| 702 | |||
| 703 | SetWindowTextA(gEditWindow, aNewString.c_str()); |
||
| 704 | } |
||
| 705 | } |
||
| 706 | break; |
||
| 707 | } |
||
| 708 | |||
| 709 | return DefWindowProc(hWnd, uMsg, wParam, lParam); |
||
| 710 | } |
||
| 711 | |||
| 712 | static void CreateProgressWindow() |
||
| 713 | { |
||
| 714 | WNDCLASSA wc; |
||
| 715 | wc.style = 0; |
||
| 716 | wc.cbClsExtra = 0; |
||
| 717 | wc.cbWndExtra = 0; |
||
| 718 | wc.hbrBackground = ::GetSysColorBrush(COLOR_BTNFACE); |
||
| 719 | wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); |
||
| 720 | wc.hIcon = ::LoadIcon(NULL, IDI_ERROR); |
||
| 721 | wc.hInstance = gHInstance; |
||
| 722 | wc.lpfnWndProc = SEHProgressWindowProc; |
||
| 723 | wc.lpszClassName = "SEHProgressWindow"; |
||
| 724 | wc.lpszMenuName = NULL; |
||
| 725 | RegisterClassA(&wc); |
||
| 726 | |||
| 727 | RECT aRect; |
||
| 728 | aRect.left = 0; |
||
| 729 | aRect.top = 0; |
||
| 730 | aRect.right = 240; |
||
| 731 | aRect.bottom = 64; |
||
| 732 | |||
| 733 | DWORD aWindowStyle = WS_CLIPCHILDREN | WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; |
||
| 734 | |||
| 735 | BOOL worked = AdjustWindowRect(&aRect, aWindowStyle, FALSE); |
||
| 736 | |||
| 737 | HWND aHWnd = CreateWindowA("SEHProgressWindow", "Submitting Report", |
||
| 738 | aWindowStyle, |
||
| 739 | 64, 64, |
||
| 740 | aRect.right - aRect.left, |
||
| 741 | aRect.bottom - aRect.top, |
||
| 742 | NULL, |
||
| 743 | NULL, |
||
| 744 | gHInstance, |
||
| 745 | 0); |
||
| 746 | |||
| 747 | // Check every 20ms to see if the transfer has completed |
||
| 748 | SetTimer(aHWnd, 0, 20, NULL); |
||
| 749 | |||
| 750 | // Every second we should change the edit text |
||
| 751 | SetTimer(aHWnd, 1, 1000, NULL); |
||
| 752 | |||
| 753 | gEditWindow = CreateWindowA("EDIT", |
||
| 754 | "Please Wait", |
||
| 755 | |||
| 756 | WS_VISIBLE | WS_CHILD | ES_READONLY, |
||
| 757 | 24, 10, |
||
| 758 | 240-8-8, |
||
| 759 | 24, |
||
| 760 | aHWnd, |
||
| 761 | NULL, |
||
| 762 | gHInstance, |
||
| 763 | 0); |
||
| 764 | if (!gUseDefaultFonts) |
||
| 765 | SendMessage(gEditWindow, WM_SETFONT, (WPARAM) SEHCatcher::mBoldFont, 0); |
||
| 766 | |||
| 767 | SEHCatcher::mNoButtonWindow = CreateWindowA("BUTTON", "Abort", |
||
| 768 | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON, |
||
| 769 | (240 - 96)/2, 64 - 22 - 6, |
||
| 770 | 96, |
||
| 771 | 22, |
||
| 772 | aHWnd, |
||
| 773 | NULL, |
||
| 774 | gHInstance, |
||
| 775 | 0); |
||
| 776 | if (!gUseDefaultFonts) |
||
| 777 | SendMessage(SEHCatcher::mNoButtonWindow, WM_SETFONT, (WPARAM) SEHCatcher::mDialogFont, 0); |
||
| 778 | |||
| 779 | ShowWindow(aHWnd, SW_NORMAL); |
||
| 780 | } |
||
| 781 | |||
| 782 | LRESULT CALLBACK SEHCatcher::SEHWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) |
||
| 783 | { |
||
| 784 | switch (uMsg) |
||
| 785 | { |
||
| 786 | case WM_COMMAND: |
||
| 787 | { |
||
| 788 | HWND hwndCtl = (HWND) lParam; |
||
| 789 | if (hwndCtl == mYesButtonWindow) |
||
| 790 | { |
||
| 791 | // Hide current window and bring up score submitting stuff |
||
| 792 | ShowWindow(hWnd, SW_HIDE); |
||
| 793 | |||
| 794 | ShowSubmitInfoDialog(); |
||
| 795 | } |
||
| 796 | else if (hwndCtl == mNoButtonWindow) |
||
| 797 | { |
||
| 798 | mExiting = true; |
||
| 799 | } |
||
| 800 | else if (hwndCtl == mDebugButtonWindow) |
||
| 801 | { |
||
| 802 | mDebugError = true; |
||
| 803 | mExiting = true; |
||
| 804 | } |
||
| 805 | } |
||
| 806 | break; |
||
| 807 | case WM_CLOSE: |
||
| 808 | mExiting = true; |
||
| 809 | return 0; |
||
| 810 | } |
||
| 811 | |||
| 812 | return DefWindowProc(hWnd, uMsg, wParam, lParam); |
||
| 813 | } |
||
| 814 | |||
| 815 | LRESULT CALLBACK SEHCatcher::SubmitInfoWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) |
||
| 816 | { |
||
| 817 | switch (uMsg) |
||
| 818 | { |
||
| 819 | case WM_COMMAND: |
||
| 820 | { |
||
| 821 | HWND hwndCtl = (HWND) lParam; |
||
| 822 | if (hwndCtl == mYesButtonWindow) |
||
| 823 | { |
||
| 824 | // Hide current window and bring up score submitting stuff |
||
| 825 | ShowWindow(hWnd, SW_HIDE); |
||
| 826 | |||
| 827 | char aStr[8192]; |
||
| 828 | GetWindowTextA(mEditWindow, aStr, 8192); |
||
| 829 | mUserText = aStr; |
||
| 830 | |||
| 831 | CreateProgressWindow(); |
||
| 832 | _beginthread(SubmitReportThread, 0, 0); |
||
| 833 | } |
||
| 834 | else if (hwndCtl == mNoButtonWindow) |
||
| 835 | { |
||
| 836 | mExiting = true; |
||
| 837 | } |
||
| 838 | } |
||
| 839 | break; |
||
| 840 | case WM_CLOSE: |
||
| 841 | mExiting = true; |
||
| 842 | return 0; |
||
| 843 | } |
||
| 844 | |||
| 845 | return DefWindowProc(hWnd, uMsg, wParam, lParam); |
||
| 846 | } |
||
| 847 | |||
| 848 | void SEHCatcher::WriteToFile(const std::string& theErrorText) |
||
| 849 | { |
||
| 850 | std::fstream aStream("crash.txt", std::ios::out); |
||
| 851 | aStream << theErrorText.c_str() << std::endl; |
||
| 852 | } |
||
| 853 | |||
| 854 | void SEHCatcher::SubmitReportThread(void *theArg) |
||
| 855 | { |
||
| 856 | std::string aSeperator = "---------------------------7d3e1f30eec"; |
||
| 857 | |||
| 858 | DefinesMap aSEHWebParams; |
||
| 859 | |||
| 860 | mApp->GetSEHWebParams(&aSEHWebParams); |
||
| 861 | |||
| 862 | std::string aContent; |
||
| 863 | |||
| 864 | DefinesMap::iterator anItr = aSEHWebParams.begin(); |
||
| 865 | while (anItr != aSEHWebParams.end()) |
||
| 866 | { |
||
| 867 | aContent += "--" + aSeperator + "\r\n" |
||
| 868 | "Content-Disposition: form-data; name=\"" + anItr->first + "\"\r\n" + |
||
| 869 | "\r\n" + |
||
| 870 | anItr->second + "\r\n"; |
||
| 871 | |||
| 872 | ++anItr; |
||
| 873 | } |
||
| 874 | |||
| 875 | aContent += |
||
| 876 | |||
| 877 | /*"--" + aSeperator + "\r\n" |
||
| 878 | "Content-Disposition: form-data; name=\"username\"\r\n" + |
||
| 879 | "\r\n" + |
||
| 880 | mApp->mUserName + "\r\n" + */ |
||
| 881 | |||
| 882 | "--" + aSeperator + "\r\n" |
||
| 883 | "Content-Disposition: form-data; name=\"prod\"\r\n" + |
||
| 884 | "\r\n" + |
||
| 885 | mApp->mProdName + "\r\n" + |
||
| 886 | |||
| 887 | "--" + aSeperator + "\r\n" |
||
| 888 | "Content-Disposition: form-data; name=\"version\"\r\n" + |
||
| 889 | "\r\n" + |
||
| 890 | mApp->mProductVersion + "\r\n" + |
||
| 891 | |||
| 892 | /*"--" + aSeperator + "\r\n" |
||
| 893 | "Content-Disposition: form-data; name=\"buildnum\"\r\n" + |
||
| 894 | "\r\n" + |
||
| 895 | StrFormat("%d", mApp->mBuildNum) + "\r\n" + |
||
| 896 | |||
| 897 | "--" + aSeperator + "\r\n" |
||
| 898 | "Content-Disposition: form-data; name=\"builddate\"\r\n" + |
||
| 899 | "\r\n" + |
||
| 900 | mApp->mBuildDate + "\r\n" + |
||
| 901 | |||
| 902 | "--" + aSeperator + "\r\n" |
||
| 903 | "Content-Disposition: form-data; name=\"referid\"\r\n" + |
||
| 904 | "\r\n" + |
||
| 905 | mApp->mReferId + "\r\n" +*/ |
||
| 906 | |||
| 907 | "--" + aSeperator + "\r\n" |
||
| 908 | "Content-Disposition: form-data; name=\"usertext\"\r\n" + |
||
| 909 | "\r\n" + |
||
| 910 | mUserText + "\r\n" + |
||
| 911 | |||
| 912 | "--" + aSeperator + "\r\n" |
||
| 913 | "Content-Disposition: form-data; name=\"errortitle\"\r\n" + |
||
| 914 | "\r\n" + |
||
| 915 | mErrorTitle + "\r\n" + |
||
| 916 | |||
| 917 | "--" + aSeperator + "\r\n" |
||
| 918 | "Content-Disposition: form-data; name=\"errortext\"\r\n" + |
||
| 919 | "\r\n" + |
||
| 920 | mErrorText + "\r\n"; |
||
| 921 | |||
| 922 | if (mHasDemoFile) |
||
| 923 | { |
||
| 924 | Buffer aBuffer; |
||
| 925 | mApp->ReadBufferFromFile(mUploadFileName, &aBuffer, true); |
||
| 926 | |||
| 927 | aContent += |
||
| 928 | "--" + aSeperator + "\r\n" |
||
| 929 | "Content-Disposition: form-data; name=\"demofile\"; filename=\"popcap.dmo\"\r\n" + |
||
| 930 | "Content-Type: application/octet-stream\r\n" + |
||
| 931 | "\r\n"; |
||
| 932 | |||
| 933 | aContent.insert(aContent.end(), (char*) aBuffer.GetDataPtr(), (char*) aBuffer.GetDataPtr() + aBuffer.GetDataLen()); |
||
| 934 | |||
| 935 | aContent += "\r\n"; |
||
| 936 | } |
||
| 937 | |||
| 938 | aContent += |
||
| 939 | "--" + aSeperator + "--\r\n"; |
||
| 940 | |||
| 941 | std::string aSendString = |
||
| 942 | "POST /deluxe_error.php HTTP/1.1\r\n" |
||
| 943 | "Content-Type: multipart/form-data; boundary=" + aSeperator + "\r\n" |
||
| 944 | "User-Agent: Mozilla/4.0 (compatible; popcap)\r\n" + |
||
| 945 | "Host: " + mSubmitHost + "\r\n" + |
||
| 946 | "Content-Length: " + StrFormat("%d", aContent.length()) + "\r\n" + |
||
| 947 | "Connection: close\r\n" + |
||
| 948 | "\r\n" + aContent; |
||
| 949 | |||
| 950 | mSubmitReportTransfer.SendRequestString(mSubmitHost, aSendString); |
||
| 951 | } |
||
| 952 | |||
| 953 | void SEHCatcher::ShowSubmitInfoDialog() |
||
| 954 | { |
||
| 955 | WNDCLASSA wc; |
||
| 956 | wc.style = 0; |
||
| 957 | wc.cbClsExtra = 0; |
||
| 958 | wc.cbWndExtra = 0; |
||
| 959 | wc.hbrBackground = ::GetSysColorBrush(COLOR_BTNFACE); |
||
| 960 | wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); |
||
| 961 | wc.hIcon = ::LoadIcon(NULL, IDI_ERROR); |
||
| 962 | wc.hInstance = gHInstance; |
||
| 963 | wc.lpfnWndProc = SubmitInfoWindowProc; |
||
| 964 | wc.lpszClassName = "SubmitInfoWindow"; |
||
| 965 | wc.lpszMenuName = NULL; |
||
| 966 | RegisterClassA(&wc); |
||
| 967 | |||
| 968 | RECT aRect; |
||
| 969 | aRect.left = 0; |
||
| 970 | aRect.top = 0; |
||
| 971 | aRect.right = 400; |
||
| 972 | aRect.bottom = 300; |
||
| 973 | |||
| 974 | DWORD aWindowStyle = WS_CLIPCHILDREN | WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; |
||
| 975 | |||
| 976 | BOOL worked = AdjustWindowRect(&aRect, aWindowStyle, FALSE); |
||
| 977 | |||
| 978 | HWND aHWnd = CreateWindowA("SubmitInfoWindow", "Error Details", |
||
| 979 | aWindowStyle, |
||
| 980 | 64+16, 64+16, |
||
| 981 | aRect.right - aRect.left, |
||
| 982 | aRect.bottom - aRect.top, |
||
| 983 | NULL, |
||
| 984 | NULL, |
||
| 985 | gHInstance, |
||
| 986 | 0); |
||
| 987 | |||
| 988 | HWND aLabelWindow = CreateWindowW(L"EDIT", mSubmitMessage.c_str(), |
||
| 989 | |||
| 990 | WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_READONLY, |
||
| 991 | 8, 8, |
||
| 992 | 400-8-8, |
||
| 993 | 84, |
||
| 994 | aHWnd, |
||
| 995 | NULL, |
||
| 996 | gHInstance, |
||
| 997 | 0); |
||
| 998 | |||
| 999 | HDC aDC = ::GetDC(aLabelWindow); |
||
| 1000 | int aFontHeight = -MulDiv(9, 96, 72); |
||
| 1001 | ::ReleaseDC(aLabelWindow, aDC); |
||
| 1002 | HFONT aBoldArialFont = CreateFontA(aFontHeight, 0, 0, 0, FW_BOLD, 0, 0, |
||
| 1003 | false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, |
||
| 1004 | DEFAULT_PITCH | FF_DONTCARE, "Arial"); |
||
| 1005 | |||
| 1006 | if (!gUseDefaultFonts) |
||
| 1007 | SendMessage(aLabelWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0); |
||
| 1008 | |||
| 1009 | mEditWindow = CreateWindowA("EDIT", "", |
||
| 1010 | WS_VISIBLE | WS_CHILD | ES_MULTILINE | WS_BORDER | WS_VSCROLL, |
||
| 1011 | 8, 300-168-24-8-8, |
||
| 1012 | 400-8-8, |
||
| 1013 | 168, |
||
| 1014 | aHWnd, |
||
| 1015 | NULL, |
||
| 1016 | gHInstance, |
||
| 1017 | 0); |
||
| 1018 | |||
| 1019 | aDC = ::GetDC(mEditWindow); |
||
| 1020 | aFontHeight = -MulDiv(8, 96, 72); |
||
| 1021 | ::ReleaseDC(mEditWindow, aDC); |
||
| 1022 | HFONT aCourierNewFont = CreateFontA(aFontHeight, 0, 0, 0, FW_NORMAL, 0, 0, |
||
| 1023 | false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, |
||
| 1024 | DEFAULT_PITCH | FF_DONTCARE, "Courier New"); |
||
| 1025 | if (!gUseDefaultFonts) |
||
| 1026 | SendMessage(mEditWindow, WM_SETFONT, (WPARAM) mDialogFont, 0); |
||
| 1027 | SetFocus(mEditWindow); |
||
| 1028 | |||
| 1029 | int aButtonWidth = (400 - 8 - 8 - 8) / 2; |
||
| 1030 | int aCurX = 8; |
||
| 1031 | |||
| 1032 | aWindowStyle = WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON; |
||
| 1033 | |||
| 1034 | if (mApp == NULL) |
||
| 1035 | aWindowStyle |= WS_DISABLED; |
||
| 1036 | |||
| 1037 | mYesButtonWindow = CreateWindowA("BUTTON", "Continue", |
||
| 1038 | aWindowStyle, |
||
| 1039 | aCurX, 300-24-8, |
||
| 1040 | aButtonWidth, |
||
| 1041 | 24, |
||
| 1042 | aHWnd, |
||
| 1043 | NULL, |
||
| 1044 | gHInstance, |
||
| 1045 | 0); |
||
| 1046 | if (!gUseDefaultFonts) |
||
| 1047 | SendMessage(mYesButtonWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0); |
||
| 1048 | |||
| 1049 | aCurX += aButtonWidth+8; |
||
| 1050 | |||
| 1051 | mNoButtonWindow = CreateWindowA("BUTTON", "Abort", |
||
| 1052 | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON, |
||
| 1053 | aCurX, 300-24-8, |
||
| 1054 | aButtonWidth, |
||
| 1055 | 24, |
||
| 1056 | aHWnd, |
||
| 1057 | NULL, |
||
| 1058 | gHInstance, |
||
| 1059 | 0); |
||
| 1060 | if (!gUseDefaultFonts) |
||
| 1061 | SendMessage(mNoButtonWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0); |
||
| 1062 | |||
| 1063 | ShowWindow(aHWnd, SW_NORMAL); |
||
| 1064 | } |
||
| 1065 | |||
| 1066 | void SEHCatcher::ShowErrorDialog(const std::string& theErrorTitle, const std::string& theErrorText) |
||
| 1067 | { |
||
| 1068 | OSVERSIONINFO aVersionInfo; |
||
| 1069 | aVersionInfo.dwOSVersionInfoSize = sizeof(aVersionInfo); |
||
| 1070 | GetVersionEx(&aVersionInfo); |
||
| 1071 | |||
| 1072 | // Setting fonts on 98 causes weirdo crash things in GDI upon the second crash. |
||
| 1073 | // That's no good. |
||
| 1074 | gUseDefaultFonts = aVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT; |
||
| 1075 | |||
| 1076 | int aHeight = -MulDiv(8, 96, 72); |
||
| 1077 | mDialogFont = ::CreateFontA(aHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, |
||
| 1078 | false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, |
||
| 1079 | DEFAULT_PITCH | FF_DONTCARE, "Tahoma"); |
||
| 1080 | |||
| 1081 | aHeight = -MulDiv(10, 96, 72); |
||
| 1082 | mBoldFont = ::CreateFontA(aHeight, 0, 0, 0, FW_BOLD, FALSE, FALSE, |
||
| 1083 | false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, |
||
| 1084 | DEFAULT_PITCH | FF_DONTCARE, "Tahoma"); |
||
| 1085 | |||
| 1086 | ::SetCursor(::LoadCursor(NULL, IDC_ARROW)); |
||
| 1087 | |||
| 1088 | mErrorTitle = theErrorTitle; |
||
| 1089 | mErrorText = theErrorText; |
||
| 1090 | |||
| 1091 | |||
| 1092 | WNDCLASSA wc; |
||
| 1093 | wc.style = 0; |
||
| 1094 | wc.cbClsExtra = 0; |
||
| 1095 | wc.cbWndExtra = 0; |
||
| 1096 | wc.hbrBackground = ::GetSysColorBrush(COLOR_BTNFACE); |
||
| 1097 | wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); |
||
| 1098 | wc.hIcon = ::LoadIcon(NULL, IDI_ERROR); |
||
| 1099 | wc.hInstance = gHInstance; |
||
| 1100 | wc.lpfnWndProc = SEHWindowProc; |
||
| 1101 | wc.lpszClassName = "SEHWindow"; |
||
| 1102 | wc.lpszMenuName = NULL; |
||
| 1103 | RegisterClassA(&wc); |
||
| 1104 | |||
| 1105 | RECT aRect; |
||
| 1106 | aRect.left = 0; |
||
| 1107 | aRect.top = 0; |
||
| 1108 | aRect.right = 400; |
||
| 1109 | aRect.bottom = 300; |
||
| 1110 | |||
| 1111 | DWORD aWindowStyle = WS_CLIPCHILDREN | WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; |
||
| 1112 | |||
| 1113 | BOOL worked = AdjustWindowRect(&aRect, aWindowStyle, FALSE); |
||
| 1114 | |||
| 1115 | HWND aHWnd = CreateWindowW(L"SEHWindow", L"Fatal Error!", |
||
| 1116 | aWindowStyle, |
||
| 1117 | 64, 64, |
||
| 1118 | aRect.right - aRect.left, |
||
| 1119 | aRect.bottom - aRect.top, |
||
| 1120 | NULL, |
||
| 1121 | NULL, |
||
| 1122 | gHInstance, |
||
| 1123 | 0); |
||
| 1124 | |||
| 1125 | HWND aLabelWindow = CreateWindowW(L"EDIT", |
||
| 1126 | mCrashMessage.c_str(), |
||
| 1127 | WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_READONLY, |
||
| 1128 | 8, 8, |
||
| 1129 | 400-8-8, |
||
| 1130 | 84, |
||
| 1131 | aHWnd, |
||
| 1132 | NULL, |
||
| 1133 | gHInstance, |
||
| 1134 | 0); |
||
| 1135 | |||
| 1136 | int aFontHeight = -MulDiv(9, 96, 72); |
||
| 1137 | HFONT aBoldArialFont = CreateFontA(aFontHeight, 0, 0, 0, FW_BOLD, 0, 0, |
||
| 1138 | false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, |
||
| 1139 | DEFAULT_PITCH | FF_DONTCARE, "Arial"); |
||
| 1140 | |||
| 1141 | if (!gUseDefaultFonts) |
||
| 1142 | SendMessage(aLabelWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0); |
||
| 1143 | |||
| 1144 | HWND anEditWindow = CreateWindowA("EDIT", theErrorText.c_str(), |
||
| 1145 | WS_VISIBLE | WS_CHILD | ES_MULTILINE | WS_BORDER | WS_VSCROLL | ES_READONLY, |
||
| 1146 | 8, 300-168-24-8-8, |
||
| 1147 | 400-8-8, |
||
| 1148 | 168, |
||
| 1149 | aHWnd, |
||
| 1150 | NULL, |
||
| 1151 | gHInstance, |
||
| 1152 | 0); |
||
| 1153 | |||
| 1154 | aFontHeight = -MulDiv(8, 96, 72); |
||
| 1155 | HFONT aCourierNewFont = CreateFontA(aFontHeight, 0, 0, 0, FW_NORMAL, 0, 0, |
||
| 1156 | false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, |
||
| 1157 | DEFAULT_PITCH | FF_DONTCARE, "Courier New"); |
||
| 1158 | if (!gUseDefaultFonts) |
||
| 1159 | SendMessage(anEditWindow, WM_SETFONT, (WPARAM) aCourierNewFont, 0); |
||
| 1160 | |||
| 1161 | aWindowStyle = WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON; |
||
| 1162 | |||
| 1163 | if (mApp == NULL) |
||
| 1164 | aWindowStyle |= WS_DISABLED; |
||
| 1165 | |||
| 1166 | #ifdef _DEBUG |
||
| 1167 | bool doDebugButton = true; |
||
| 1168 | #else |
||
| 1169 | bool doDebugButton = false; |
||
| 1170 | #endif |
||
| 1171 | |||
| 1172 | bool canSubmit = mAllowSubmit && !mSubmitHost.empty(); |
||
| 1173 | int aNumButtons = 1 + (doDebugButton ? 1 : 0) + (canSubmit ? 1 : 0); |
||
| 1174 | |||
| 1175 | int aButtonWidth = (400 - 8 - 8 - (aNumButtons - 1)*8) / aNumButtons; |
||
| 1176 | |||
| 1177 | int aCurX = 8; |
||
| 1178 | |||
| 1179 | if (canSubmit) |
||
| 1180 | { |
||
| 1181 | mYesButtonWindow = CreateWindowA("BUTTON", "Send Report", |
||
| 1182 | aWindowStyle, |
||
| 1183 | aCurX, 300-24-8, |
||
| 1184 | aButtonWidth, |
||
| 1185 | 24, |
||
| 1186 | aHWnd, |
||
| 1187 | NULL, |
||
| 1188 | gHInstance, |
||
| 1189 | 0); |
||
| 1190 | if (!gUseDefaultFonts) |
||
| 1191 | SendMessage(mYesButtonWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0); |
||
| 1192 | |||
| 1193 | aCurX += aButtonWidth + 8; |
||
| 1194 | } |
||
| 1195 | |||
| 1196 | if (doDebugButton) |
||
| 1197 | { |
||
| 1198 | mDebugButtonWindow = CreateWindowA("BUTTON", "Debug", |
||
| 1199 | aWindowStyle, |
||
| 1200 | aCurX, 300-24-8, |
||
| 1201 | aButtonWidth, |
||
| 1202 | 24, |
||
| 1203 | aHWnd, |
||
| 1204 | NULL, |
||
| 1205 | gHInstance, |
||
| 1206 | 0); |
||
| 1207 | if (!gUseDefaultFonts) |
||
| 1208 | SendMessage(mDebugButtonWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0); |
||
| 1209 | |||
| 1210 | aCurX += aButtonWidth + 8; |
||
| 1211 | } |
||
| 1212 | |||
| 1213 | mNoButtonWindow = CreateWindowA("BUTTON", "Close Now", |
||
| 1214 | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON, |
||
| 1215 | aCurX, 300-24-8, |
||
| 1216 | aButtonWidth, |
||
| 1217 | 24, |
||
| 1218 | aHWnd, |
||
| 1219 | NULL, |
||
| 1220 | gHInstance, |
||
| 1221 | 0); |
||
| 1222 | |||
| 1223 | if (!gUseDefaultFonts) |
||
| 1224 | SendMessage(mNoButtonWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0); |
||
| 1225 | |||
| 1226 | ShowWindow(aHWnd, SW_NORMAL); |
||
| 1227 | |||
| 1228 | MSG msg; |
||
| 1229 | while ((GetMessage(&msg, NULL, 0, 0) > 0) && (!mExiting)) |
||
| 1230 | { |
||
| 1231 | TranslateMessage(&msg); |
||
| 1232 | DispatchMessage(&msg); |
||
| 1233 | } |
||
| 1234 | |||
| 1235 | DestroyWindow(aHWnd); |
||
| 1236 | |||
| 1237 | DeleteObject(mDialogFont); |
||
| 1238 | DeleteObject(mBoldFont); |
||
| 1239 | DeleteObject(aBoldArialFont); |
||
| 1240 | DeleteObject(aCourierNewFont); |
||
| 1241 | } |
||
| 1242 | |||
| 1243 | std::string SEHCatcher::GetSysInfo() |
||
| 1244 | { |
||
| 1245 | std::string aDebugDump; |
||
| 1246 | |||
| 1247 | OSVERSIONINFOA aVersionInfo; |
||
| 1248 | aVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); |
||
| 1249 | GetVersionExA(&aVersionInfo); |
||
| 1250 | |||
| 1251 | aDebugDump += "Windows Ver: "; |
||
| 1252 | if (aVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) |
||
| 1253 | aDebugDump += "NT "; |
||
| 1254 | else |
||
| 1255 | aDebugDump += "9x "; |
||
| 1256 | |||
| 1257 | char aVersionStr[20]; |
||
| 1258 | sprintf(aVersionStr, "%d.%d", aVersionInfo.dwMajorVersion, aVersionInfo.dwMinorVersion); |
||
| 1259 | aDebugDump += aVersionStr; |
||
| 1260 | aDebugDump += " "; |
||
| 1261 | aDebugDump += aVersionInfo.szCSDVersion; |
||
| 1262 | aDebugDump += " "; |
||
| 1263 | sprintf(aVersionStr, "%d", aVersionInfo.dwBuildNumber); |
||
| 1264 | aDebugDump += "Build "; |
||
| 1265 | aDebugDump += aVersionStr; |
||
| 1266 | aDebugDump += "\r\n"; |
||
| 1267 | |||
| 1268 | HMODULE aMod; |
||
| 1269 | char aPath[256]; |
||
| 1270 | |||
| 1271 | if (mApp != NULL) |
||
| 1272 | { |
||
| 1273 | aMod = LoadLibraryA("ddraw.dll"); |
||
| 1274 | if (aMod != NULL) |
||
| 1275 | { |
||
| 1276 | GetModuleFileNameA(aMod, aPath, 256); |
||
| 1277 | aDebugDump += "DDraw Ver: " + mApp->GetProductVersion(aPath) + "\r\n"; |
||
| 1278 | FreeLibrary(aMod); |
||
| 1279 | } |
||
| 1280 | |||
| 1281 | aMod = LoadLibraryA("dsound.dll"); |
||
| 1282 | if (aMod != NULL) |
||
| 1283 | { |
||
| 1284 | GetModuleFileNameA(aMod, aPath, 256); |
||
| 1285 | aDebugDump += "DSound Ver: " + mApp->GetProductVersion(aPath) + "\r\n"; |
||
| 1286 | FreeLibrary(aMod); |
||
| 1287 | } |
||
| 1288 | } |
||
| 1289 | |||
| 1290 | return aDebugDump; |
||
| 1291 | } |
||
| 1292 |