Debug.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: Debug.cpp
  24. //-----------------------------------------------------------------------------
  25. //
  26. // Westwood Studios Pacific.
  27. //
  28. // Confidential Information
  29. // Copyright (C) 2001 - All Rights Reserved
  30. //
  31. //-----------------------------------------------------------------------------
  32. //
  33. // Project: RTS3
  34. //
  35. // File name: Debug.cpp
  36. //
  37. // Created: Steven Johnson, August 2001
  38. //
  39. // Desc: Debug logging and other debug utilities
  40. //
  41. // ----------------------------------------------------------------------------
  42. // SYSTEM INCLUDES
  43. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  44. // USER INCLUDES
  45. #define DEBUG_THREADSAFE
  46. #ifdef DEBUG_THREADSAFE
  47. #include "Common/CriticalSection.h"
  48. #endif
  49. #include "Common/Debug.h"
  50. #include "Common/SystemInfo.h"
  51. #include "Common/UnicodeString.h"
  52. #include "GameClient/GameText.h"
  53. #include "GameClient/Keyboard.h"
  54. #include "GameClient/Mouse.h"
  55. #if defined(DEBUG_STACKTRACE) || defined(IG_DEBUG_STACKTRACE)
  56. #include "Common/StackDump.h"
  57. #endif
  58. // Horrible reference, but we really, really need to know if we are windowed.
  59. extern bool DX8Wrapper_IsWindowed;
  60. extern HWND ApplicationHWnd;
  61. extern char *gAppPrefix; /// So WB can have a different log file name.
  62. #ifdef _INTERNAL
  63. // this should ALWAYS be present
  64. #pragma optimize("", off)
  65. #endif
  66. // ----------------------------------------------------------------------------
  67. // DEFINES
  68. // ----------------------------------------------------------------------------
  69. #ifdef DEBUG_LOGGING
  70. #if defined(_INTERNAL)
  71. #define DEBUG_FILE_NAME "DebugLogFileI.txt"
  72. #define DEBUG_FILE_NAME_PREV "DebugLogFilePrevI.txt"
  73. #elif defined(_DEBUG)
  74. #define DEBUG_FILE_NAME "DebugLogFileD.txt"
  75. #define DEBUG_FILE_NAME_PREV "DebugLogFilePrevD.txt"
  76. #else
  77. #define DEBUG_FILE_NAME "DebugLogFile.txt"
  78. #define DEBUG_FILE_NAME_PREV "DebugLogFilePrev.txt"
  79. #endif
  80. #endif
  81. // ----------------------------------------------------------------------------
  82. // PRIVATE TYPES
  83. // ----------------------------------------------------------------------------
  84. // ----------------------------------------------------------------------------
  85. // PRIVATE DATA
  86. // ----------------------------------------------------------------------------
  87. #ifdef DEBUG_LOGGING
  88. static FILE *theLogFile = NULL;
  89. #endif
  90. #define LARGE_BUFFER 8192
  91. static char theBuffer[ LARGE_BUFFER ]; // make it big to avoid weird overflow bugs in debug mode
  92. static int theDebugFlags = 0;
  93. static DWORD theMainThreadID = 0;
  94. // ----------------------------------------------------------------------------
  95. // PUBLIC DATA
  96. // ----------------------------------------------------------------------------
  97. char* TheCurrentIgnoreCrashPtr = NULL;
  98. // ----------------------------------------------------------------------------
  99. // PRIVATE PROTOTYPES
  100. // ----------------------------------------------------------------------------
  101. static const char *getCurrentTimeString(void);
  102. static const char *getCurrentTickString(void);
  103. static const char *prepBuffer(const char* format, char *buffer);
  104. #ifdef DEBUG_LOGGING
  105. static void doLogOutput(const char *buffer);
  106. #endif
  107. static int doCrashBox(const char *buffer, Bool logResult);
  108. static void whackFunnyCharacters(char *buf);
  109. #ifdef DEBUG_STACKTRACE
  110. static void doStackDump();
  111. #endif
  112. // ----------------------------------------------------------------------------
  113. // PRIVATE FUNCTIONS
  114. // ----------------------------------------------------------------------------
  115. // ----------------------------------------------------------------------------
  116. inline Bool ignoringAsserts()
  117. {
  118. #if defined(_DEBUG) || defined(_INTERNAL)
  119. return !DX8Wrapper_IsWindowed || (TheGlobalData&&TheGlobalData->m_debugIgnoreAsserts);
  120. #else
  121. return !DX8Wrapper_IsWindowed;
  122. #endif
  123. }
  124. // ----------------------------------------------------------------------------
  125. inline HWND getThreadHWND()
  126. {
  127. return (theMainThreadID == GetCurrentThreadId())?ApplicationHWnd:NULL;
  128. }
  129. // ----------------------------------------------------------------------------
  130. int MessageBoxWrapper( LPCSTR lpText, LPCSTR lpCaption, UINT uType )
  131. {
  132. HWND threadHWND = getThreadHWND();
  133. if (!threadHWND)
  134. return (uType & MB_ABORTRETRYIGNORE)?IDIGNORE:IDYES;
  135. return ::MessageBox(threadHWND, lpText, lpCaption, uType);
  136. }
  137. // ----------------------------------------------------------------------------
  138. // getCurrentTimeString
  139. /**
  140. Return the current time in string form
  141. */
  142. // ----------------------------------------------------------------------------
  143. static const char *getCurrentTimeString(void)
  144. {
  145. time_t aclock;
  146. time(&aclock);
  147. struct tm *newtime = localtime(&aclock);
  148. return asctime(newtime);
  149. }
  150. // ----------------------------------------------------------------------------
  151. // getCurrentTickString
  152. /**
  153. Return the current TickCount in string form
  154. */
  155. // ----------------------------------------------------------------------------
  156. static const char *getCurrentTickString(void)
  157. {
  158. static char TheTickString[32];
  159. sprintf(TheTickString, "(T=%08lx)",::GetTickCount());
  160. return TheTickString;
  161. }
  162. // ----------------------------------------------------------------------------
  163. // prepBuffer
  164. // zap the buffer and optionally prepend the tick time.
  165. // ----------------------------------------------------------------------------
  166. /**
  167. Empty the buffer passed in, then optionally prepend the current TickCount
  168. value in string form, depending on the setting of theDebugFlags.
  169. */
  170. static const char *prepBuffer(const char* format, char *buffer)
  171. {
  172. buffer[0] = 0;
  173. #ifdef ALLOW_DEBUG_UTILS
  174. if (theDebugFlags & DEBUG_FLAG_PREPEND_TIME)
  175. {
  176. strcpy(buffer, getCurrentTickString());
  177. strcat(buffer, " ");
  178. }
  179. #endif
  180. return format;
  181. }
  182. // ----------------------------------------------------------------------------
  183. // doLogOutput
  184. /**
  185. send a string directly to the log file and/or console without further processing.
  186. */
  187. // ----------------------------------------------------------------------------
  188. #ifdef DEBUG_LOGGING
  189. static void doLogOutput(const char *buffer)
  190. {
  191. // log message to file
  192. if (theDebugFlags & DEBUG_FLAG_LOG_TO_FILE)
  193. {
  194. if (theLogFile)
  195. {
  196. fprintf(theLogFile, "%s", buffer); // note, no \n (should be there already)
  197. fflush(theLogFile);
  198. }
  199. }
  200. // log message to dev studio output window
  201. if (theDebugFlags & DEBUG_FLAG_LOG_TO_CONSOLE)
  202. {
  203. ::OutputDebugString(buffer);
  204. }
  205. }
  206. #endif
  207. // ----------------------------------------------------------------------------
  208. // doCrashBox
  209. /*
  210. present a messagebox with the given message. Depending on user selection,
  211. we exit the app, break into debugger, or continue execution.
  212. */
  213. // ----------------------------------------------------------------------------
  214. static int doCrashBox(const char *buffer, Bool logResult)
  215. {
  216. int result;
  217. if (!ignoringAsserts()) {
  218. result = MessageBoxWrapper(buffer, "Assertion Failure", MB_ABORTRETRYIGNORE|MB_TASKMODAL|MB_ICONWARNING|MB_DEFBUTTON3);
  219. //result = MessageBoxWrapper(buffer, "Assertion Failure", MB_ABORTRETRYIGNORE|MB_TASKMODAL|MB_ICONWARNING);
  220. } else {
  221. result = IDIGNORE;
  222. }
  223. switch(result)
  224. {
  225. case IDABORT:
  226. #ifdef DEBUG_LOGGING
  227. if (logResult)
  228. DebugLog("[Abort]\n");
  229. #endif
  230. _exit(1);
  231. break;
  232. case IDRETRY:
  233. #ifdef DEBUG_LOGGING
  234. if (logResult)
  235. DebugLog("[Retry]\n");
  236. #endif
  237. ::DebugBreak();
  238. break;
  239. case IDIGNORE:
  240. #ifdef DEBUG_LOGGING
  241. // do nothing, just keep going
  242. if (logResult)
  243. DebugLog("[Ignore]\n");
  244. #endif
  245. break;
  246. }
  247. return result;
  248. }
  249. #ifdef DEBUG_STACKTRACE
  250. // ----------------------------------------------------------------------------
  251. /**
  252. Dumps a stack trace (from the current PC) to logfile and/or console.
  253. */
  254. static void doStackDump()
  255. {
  256. const int STACKTRACE_SIZE = 24;
  257. const int STACKTRACE_SKIP = 2;
  258. void* stacktrace[STACKTRACE_SIZE];
  259. doLogOutput("\nStack Dump:\n");
  260. ::FillStackAddresses(stacktrace, STACKTRACE_SIZE, STACKTRACE_SKIP);
  261. ::StackDumpFromAddresses(stacktrace, STACKTRACE_SIZE, doLogOutput);
  262. }
  263. #endif
  264. // ----------------------------------------------------------------------------
  265. // whackFunnyCharacters
  266. /**
  267. Eliminates any undesirable nonprinting characters, aside from newline,
  268. replacing them with spaces.
  269. */
  270. // ----------------------------------------------------------------------------
  271. static void whackFunnyCharacters(char *buf)
  272. {
  273. for (char *p = buf + strlen(buf) - 1; p >= buf; --p)
  274. {
  275. // ok, these are naughty magic numbers, but I'm guessing you know ASCII....
  276. if (*p >= 0 && *p < 32 && *p != 10 && *p != 13)
  277. *p = 32;
  278. }
  279. }
  280. // ----------------------------------------------------------------------------
  281. // PUBLIC FUNCTIONS
  282. // ----------------------------------------------------------------------------
  283. // ----------------------------------------------------------------------------
  284. // DebugInit
  285. // ----------------------------------------------------------------------------
  286. #ifdef ALLOW_DEBUG_UTILS
  287. /**
  288. Initialize the debug utilities. This should be called once, as near to the
  289. start of the app as possible, before anything else (since other code will
  290. probably want to make use of it).
  291. */
  292. void DebugInit(int flags)
  293. {
  294. // if (theDebugFlags != 0)
  295. // ::MessageBox(NULL, "Debug already inited", "", MB_OK|MB_APPLMODAL);
  296. // just quietly allow multiple calls to this, so that static ctors can call it.
  297. if (theDebugFlags == 0)
  298. {
  299. theDebugFlags = flags;
  300. theMainThreadID = GetCurrentThreadId();
  301. #ifdef DEBUG_LOGGING
  302. char dirbuf[ _MAX_PATH ];
  303. ::GetModuleFileName( NULL, dirbuf, sizeof( dirbuf ) );
  304. char *pEnd = dirbuf + strlen( dirbuf );
  305. while( pEnd != dirbuf )
  306. {
  307. if( *pEnd == '\\' )
  308. {
  309. *(pEnd + 1) = 0;
  310. break;
  311. }
  312. pEnd--;
  313. }
  314. char prevbuf[ _MAX_PATH ];
  315. char curbuf[ _MAX_PATH ];
  316. strcpy(prevbuf, dirbuf);
  317. strcat(prevbuf, gAppPrefix);
  318. strcat(prevbuf, DEBUG_FILE_NAME_PREV);
  319. strcpy(curbuf, dirbuf);
  320. strcat(curbuf, gAppPrefix);
  321. strcat(curbuf, DEBUG_FILE_NAME);
  322. remove(prevbuf);
  323. rename(curbuf, prevbuf);
  324. theLogFile = fopen(curbuf, "w");
  325. if (theLogFile != NULL)
  326. {
  327. DebugLog("Log %s opened: %s\n", curbuf, getCurrentTimeString());
  328. }
  329. #endif
  330. }
  331. }
  332. #endif
  333. // ----------------------------------------------------------------------------
  334. // DebugLog
  335. // ----------------------------------------------------------------------------
  336. #ifdef DEBUG_LOGGING
  337. /**
  338. Print a character string to the logfile and/or console.
  339. */
  340. void DebugLog(const char *format, ...)
  341. {
  342. #ifdef DEBUG_THREADSAFE
  343. ScopedCriticalSection scopedCriticalSection(TheDebugLogCriticalSection);
  344. #endif
  345. if (theDebugFlags == 0)
  346. MessageBoxWrapper("DebugLog - Debug not inited properly", "", MB_OK|MB_TASKMODAL);
  347. format = prepBuffer(format, theBuffer);
  348. va_list arg;
  349. va_start(arg, format);
  350. vsprintf(theBuffer + strlen(theBuffer), format, arg);
  351. va_end(arg);
  352. if (strlen(theBuffer) >= sizeof(theBuffer))
  353. MessageBoxWrapper("String too long for debug buffer", "", MB_OK|MB_TASKMODAL);
  354. whackFunnyCharacters(theBuffer);
  355. doLogOutput(theBuffer);
  356. }
  357. #endif
  358. // ----------------------------------------------------------------------------
  359. // DebugCrash
  360. // ----------------------------------------------------------------------------
  361. #ifdef DEBUG_CRASHING
  362. /**
  363. Print a character string to the logfile and/or console, then halt execution
  364. while presenting the user with an exit/debug/ignore dialog containing the same
  365. text message.
  366. */
  367. void DebugCrash(const char *format, ...)
  368. {
  369. // Note: You might want to make this thread safe, but we cannot. The reason is that
  370. // there is an implicit requirement on other threads that the message loop be running.
  371. // make it not static so that it'll be thread-safe.
  372. // make it big to avoid weird overflow bugs in debug mode
  373. char theCrashBuffer[ LARGE_BUFFER ];
  374. if (theDebugFlags == 0)
  375. {
  376. if (!DX8Wrapper_IsWindowed) {
  377. if (ApplicationHWnd) {
  378. ShowWindow(ApplicationHWnd, SW_HIDE);
  379. }
  380. }
  381. MessageBoxWrapper("DebugCrash - Debug not inited properly", "", MB_OK|MB_TASKMODAL);
  382. }
  383. format = prepBuffer(format, theCrashBuffer);
  384. strcat(theCrashBuffer, "ASSERTION FAILURE: ");
  385. va_list arg;
  386. va_start(arg, format);
  387. vsprintf(theCrashBuffer + strlen(theCrashBuffer), format, arg);
  388. va_end(arg);
  389. if (strlen(theCrashBuffer) >= sizeof(theCrashBuffer))
  390. {
  391. if (!DX8Wrapper_IsWindowed) {
  392. if (ApplicationHWnd) {
  393. ShowWindow(ApplicationHWnd, SW_HIDE);
  394. }
  395. }
  396. MessageBoxWrapper("String too long for debug buffers", "", MB_OK|MB_TASKMODAL);
  397. }
  398. #ifdef DEBUG_LOGGING
  399. if (ignoringAsserts())
  400. {
  401. doLogOutput("**** CRASH IN FULL SCREEN - Auto-ignored, CHECK THIS LOG!\n");
  402. }
  403. whackFunnyCharacters(theCrashBuffer);
  404. doLogOutput(theCrashBuffer);
  405. #endif
  406. #ifdef DEBUG_STACKTRACE
  407. if (!(TheGlobalData && TheGlobalData->m_debugIgnoreStackTrace))
  408. {
  409. doStackDump();
  410. }
  411. #endif
  412. strcat(theCrashBuffer, "\n\nAbort->exception; Retry->debugger; Ignore->continue\n");
  413. int result = doCrashBox(theCrashBuffer, true);
  414. if (result == IDIGNORE && TheCurrentIgnoreCrashPtr != NULL)
  415. {
  416. int yn;
  417. if (!ignoringAsserts())
  418. {
  419. yn = MessageBoxWrapper("Ignore this crash from now on?", "", MB_YESNO|MB_TASKMODAL);
  420. }
  421. else
  422. {
  423. yn = IDYES;
  424. }
  425. if (yn == IDYES)
  426. *TheCurrentIgnoreCrashPtr = 1;
  427. if( TheKeyboard )
  428. TheKeyboard->resetKeys();
  429. if( TheMouse )
  430. TheMouse->reset();
  431. }
  432. }
  433. #endif
  434. // ----------------------------------------------------------------------------
  435. // DebugShutdown
  436. // ----------------------------------------------------------------------------
  437. #ifdef ALLOW_DEBUG_UTILS
  438. /**
  439. Shut down the debug utilities. This should be called once, as near to the
  440. end of the app as possible, after everything else (since other code will
  441. probably want to make use of it).
  442. */
  443. void DebugShutdown()
  444. {
  445. #ifdef DEBUG_LOGGING
  446. if (theLogFile)
  447. {
  448. DebugLog("Log closed: %s\n", getCurrentTimeString());
  449. fclose(theLogFile);
  450. }
  451. theLogFile = NULL;
  452. #endif
  453. theDebugFlags = 0;
  454. }
  455. // ----------------------------------------------------------------------------
  456. // DebugGetFlags
  457. // ----------------------------------------------------------------------------
  458. /**
  459. Get the current values for the flags passed to DebugInit. Most code will never
  460. need to use this; the most common usage would be to temporarily enable or disable
  461. the DEBUG_FLAG_PREPEND_TIME bit for complex logfile messages.
  462. */
  463. int DebugGetFlags()
  464. {
  465. return theDebugFlags;
  466. }
  467. // ----------------------------------------------------------------------------
  468. // DebugSetFlags
  469. // ----------------------------------------------------------------------------
  470. /**
  471. Set the current values for the flags passed to DebugInit. Most code will never
  472. need to use this; the most common usage would be to temporarily enable or disable
  473. the DEBUG_FLAG_PREPEND_TIME bit for complex logfile messages.
  474. */
  475. void DebugSetFlags(int flags)
  476. {
  477. theDebugFlags = flags;
  478. }
  479. #endif // ALLOW_DEBUG_UTILS
  480. #ifdef DEBUG_PROFILE
  481. // ----------------------------------------------------------------------------
  482. SimpleProfiler::SimpleProfiler()
  483. {
  484. QueryPerformanceFrequency((LARGE_INTEGER*)&m_freq);
  485. m_startThisSession = 0;
  486. m_totalThisSession = 0;
  487. m_totalAllSessions = 0;
  488. m_numSessions = 0;
  489. }
  490. // ----------------------------------------------------------------------------
  491. void SimpleProfiler::start()
  492. {
  493. DEBUG_ASSERTCRASH(m_startThisSession == 0, ("already started"));
  494. QueryPerformanceCounter((LARGE_INTEGER*)&m_startThisSession);
  495. }
  496. // ----------------------------------------------------------------------------
  497. void SimpleProfiler::stop()
  498. {
  499. if (m_startThisSession != 0)
  500. {
  501. __int64 stop;
  502. QueryPerformanceCounter((LARGE_INTEGER*)&stop);
  503. m_totalThisSession = stop - m_startThisSession;
  504. m_totalAllSessions += stop - m_startThisSession;
  505. m_startThisSession = 0;
  506. ++m_numSessions;
  507. }
  508. }
  509. // ----------------------------------------------------------------------------
  510. void SimpleProfiler::stopAndLog(const char *msg, int howOftenToLog, int howOftenToResetAvg)
  511. {
  512. stop();
  513. // howOftenToResetAvg==0 means "never reset"
  514. if (howOftenToResetAvg > 0 && m_numSessions >= howOftenToResetAvg)
  515. {
  516. m_numSessions = 0;
  517. m_totalAllSessions = 0;
  518. DEBUG_LOG(("%s: reset averages\n",msg));
  519. }
  520. DEBUG_ASSERTLOG(m_numSessions % howOftenToLog != 0, ("%s: %f msec, total %f msec, avg %f msec\n",msg,getTime(),getTotalTime(),getAverageTime()));
  521. }
  522. // ----------------------------------------------------------------------------
  523. double SimpleProfiler::getTime()
  524. {
  525. stop();
  526. return (double)m_totalThisSession / (double)m_freq * 1000.0;
  527. }
  528. // ----------------------------------------------------------------------------
  529. int SimpleProfiler::getNumSessions()
  530. {
  531. stop();
  532. return m_numSessions;
  533. }
  534. // ----------------------------------------------------------------------------
  535. double SimpleProfiler::getTotalTime()
  536. {
  537. stop();
  538. if (!m_numSessions)
  539. return 0.0;
  540. return (double)m_totalAllSessions * 1000.0 / ((double)m_freq);
  541. }
  542. // ----------------------------------------------------------------------------
  543. double SimpleProfiler::getAverageTime()
  544. {
  545. stop();
  546. if (!m_numSessions)
  547. return 0.0;
  548. return (double)m_totalAllSessions * 1000.0 / ((double)m_freq * (double)m_numSessions);
  549. }
  550. #endif // ALLOW_DEBUG_UTILS
  551. // ----------------------------------------------------------------------------
  552. // ReleaseCrash
  553. // ----------------------------------------------------------------------------
  554. /**
  555. Halt the application, EVEN IN FINAL RELEASE BUILDS. This should be called
  556. only when a crash is guaranteed by continuing, and no meaningful continuation
  557. of processing is possible, even by throwing an exception.
  558. */
  559. #define RELEASECRASH_FILE_NAME "ReleaseCrashInfo.txt"
  560. #define RELEASECRASH_FILE_NAME_PREV "ReleaseCrashInfoPrev.txt"
  561. static FILE *theReleaseCrashLogFile = NULL;
  562. static void releaseCrashLogOutput(const char *buffer)
  563. {
  564. if (theReleaseCrashLogFile)
  565. {
  566. fprintf(theReleaseCrashLogFile, "%s", buffer); // note, no \n (should be there already)
  567. fflush(theReleaseCrashLogFile);
  568. }
  569. }
  570. void ReleaseCrash(const char *reason)
  571. {
  572. /// do additional reporting on the crash, if possible
  573. if (!DX8Wrapper_IsWindowed) {
  574. if (ApplicationHWnd) {
  575. ShowWindow(ApplicationHWnd, SW_HIDE);
  576. }
  577. }
  578. //#if defined(_DEBUG) || defined(_INTERNAL)
  579. // /* static */ char buff[8192]; // not so static so we can be threadsafe
  580. // _snprintf(buff, 8192, "Sorry, a serious error occurred. (%s)", reason);/
  581. // buff[8191] = 0;
  582. // ::MessageBox(NULL, buff, "Technical Difficulties...", MB_OK|MB_SYSTEMMODAL|MB_ICONERROR);
  583. //#else
  584. // ::MessageBox(NULL, "Sorry, a serious error occurred.", "Technical Difficulties...", MB_OK|MB_TASKMODAL|MB_ICONERROR);
  585. //#endif
  586. char prevbuf[ _MAX_PATH ];
  587. char curbuf[ _MAX_PATH ];
  588. if (TheGlobalData==NULL) {
  589. return; // We are shutting down, and TheGlobalData has been freed. jba. [4/15/2003]
  590. }
  591. strcpy(prevbuf, TheGlobalData->getPath_UserData().str());
  592. strcat(prevbuf, RELEASECRASH_FILE_NAME_PREV);
  593. strcpy(curbuf, TheGlobalData->getPath_UserData().str());
  594. strcat(curbuf, RELEASECRASH_FILE_NAME);
  595. remove(prevbuf);
  596. rename(curbuf, prevbuf);
  597. theReleaseCrashLogFile = fopen(curbuf, "w");
  598. if (theReleaseCrashLogFile)
  599. {
  600. fprintf(theReleaseCrashLogFile, "Release Crash at %s; Reason %s\n", getCurrentTimeString(), reason);
  601. fprintf(theReleaseCrashLogFile, "\nLast error:\n%s\n\nCurrent stack:\n", g_LastErrorDump.str());
  602. const int STACKTRACE_SIZE = 12;
  603. const int STACKTRACE_SKIP = 6;
  604. void* stacktrace[STACKTRACE_SIZE];
  605. ::FillStackAddresses(stacktrace, STACKTRACE_SIZE, STACKTRACE_SKIP);
  606. ::StackDumpFromAddresses(stacktrace, STACKTRACE_SIZE, releaseCrashLogOutput);
  607. fflush(theReleaseCrashLogFile);
  608. fclose(theReleaseCrashLogFile);
  609. theReleaseCrashLogFile = NULL;
  610. }
  611. if (!DX8Wrapper_IsWindowed) {
  612. if (ApplicationHWnd) {
  613. ShowWindow(ApplicationHWnd, SW_HIDE);
  614. }
  615. }
  616. #if defined(_DEBUG) || defined(_INTERNAL)
  617. /* static */ char buff[8192]; // not so static so we can be threadsafe
  618. _snprintf(buff, 8192, "Sorry, a serious error occurred. (%s)", reason);
  619. buff[8191] = 0;
  620. ::MessageBox(NULL, buff, "Technical Difficulties...", MB_OK|MB_SYSTEMMODAL|MB_ICONERROR);
  621. #else
  622. // crash error messaged changed 3/6/03 BGC
  623. // ::MessageBox(NULL, "Sorry, a serious error occurred.", "Technical Difficulties...", MB_OK|MB_TASKMODAL|MB_ICONERROR);
  624. // ::MessageBox(NULL, "You have encountered a serious error. Serious errors can be caused by many things including viruses, overheated hardware and hardware that does not meet the minimum specifications for the game. Please visit the forums at www.generals.ea.com for suggested courses of action or consult your manual for Technical Support contact information.", "Technical Difficulties...", MB_OK|MB_TASKMODAL|MB_ICONERROR);
  625. // crash error message changed again 8/22/03 M Lorenzen... made this message box modal to the system so it will appear on top of any task-modal windows, splash-screen, etc.
  626. ::MessageBox(NULL, "You have encountered a serious error. Serious errors can be caused by many things including viruses, overheated hardware and hardware that does not meet the minimum specifications for the game. Please visit the forums at www.generals.ea.com for suggested courses of action or consult your manual for Technical Support contact information.",
  627. "Technical Difficulties...",
  628. MB_OK|MB_SYSTEMMODAL|MB_ICONERROR);
  629. #endif
  630. _exit(1);
  631. }
  632. void ReleaseCrashLocalized(const AsciiString& p, const AsciiString& m)
  633. {
  634. if (!TheGameText) {
  635. ReleaseCrash(m.str());
  636. // This won't ever return
  637. return;
  638. }
  639. UnicodeString prompt = TheGameText->fetch(p);
  640. UnicodeString mesg = TheGameText->fetch(m);
  641. /// do additional reporting on the crash, if possible
  642. if (!DX8Wrapper_IsWindowed) {
  643. if (ApplicationHWnd) {
  644. ShowWindow(ApplicationHWnd, SW_HIDE);
  645. }
  646. }
  647. if (TheSystemIsUnicode)
  648. {
  649. ::MessageBoxW(NULL, mesg.str(), prompt.str(), MB_OK|MB_SYSTEMMODAL|MB_ICONERROR);
  650. }
  651. else
  652. {
  653. // However, if we're using the default version of the message box, we need to
  654. // translate the string into an AsciiString
  655. AsciiString promptA, mesgA;
  656. promptA.translate(prompt);
  657. mesgA.translate(mesg);
  658. //Make sure main window is not TOP_MOST
  659. ::SetWindowPos(ApplicationHWnd, HWND_NOTOPMOST, 0, 0, 0, 0,SWP_NOSIZE |SWP_NOMOVE);
  660. ::MessageBoxA(NULL, mesgA.str(), promptA.str(), MB_OK|MB_TASKMODAL|MB_ICONERROR);
  661. }
  662. char prevbuf[ _MAX_PATH ];
  663. char curbuf[ _MAX_PATH ];
  664. strcpy(prevbuf, TheGlobalData->getPath_UserData().str());
  665. strcat(prevbuf, RELEASECRASH_FILE_NAME_PREV);
  666. strcpy(curbuf, TheGlobalData->getPath_UserData().str());
  667. strcat(curbuf, RELEASECRASH_FILE_NAME);
  668. remove(prevbuf);
  669. rename(curbuf, prevbuf);
  670. theReleaseCrashLogFile = fopen(curbuf, "w");
  671. if (theReleaseCrashLogFile)
  672. {
  673. fprintf(theReleaseCrashLogFile, "Release Crash at %s; Reason %s\n", getCurrentTimeString(), mesg.str());
  674. const int STACKTRACE_SIZE = 12;
  675. const int STACKTRACE_SKIP = 6;
  676. void* stacktrace[STACKTRACE_SIZE];
  677. ::FillStackAddresses(stacktrace, STACKTRACE_SIZE, STACKTRACE_SKIP);
  678. ::StackDumpFromAddresses(stacktrace, STACKTRACE_SIZE, releaseCrashLogOutput);
  679. fflush(theReleaseCrashLogFile);
  680. fclose(theReleaseCrashLogFile);
  681. theReleaseCrashLogFile = NULL;
  682. }
  683. _exit(1);
  684. }