DumpException.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. // ExceptionHandler.cpp Version 1.5
  2. //
  3. // Copyright 1998 Bruce Dawson
  4. //
  5. // This source file contains the exception handler for recording error
  6. // information after crashes. See ExceptionHandler.h for information
  7. // on how to hook it in.
  8. //
  9. // Author: Bruce Dawson
  10. // brucedawson@cygnus-software.com
  11. //
  12. // Latest Modified by: Xiel
  13. //
  14. // Modified by: Hans Dietrich
  15. // hdietrich2@hotmail.com
  16. //
  17. // Version 1.5 - remove TChar support, change log file's name
  18. // Version 1.4: - Added invocation of XCrashReport.exe
  19. //
  20. // Version 1.3: - Added minidump output
  21. //
  22. // Version 1.1: - reformatted output for XP-like error report
  23. // - added ascii output to stack dump
  24. //
  25. // A paper by the original author can be found at:
  26. // http://www.cygnus-software.com/papers/release_debugging.html
  27. //
  28. ///////////////////////////////////////////////////////////////////////////////
  29. #ifdef _WIN32
  30. // Disable warnings generated by the Windows header files.
  31. #pragma warning(disable : 4514)
  32. #pragma warning(disable : 4201)
  33. #endif //_WIN32
  34. #include "precompile.h"
  35. #include "DumpException.h"
  36. #include <winpr/exception.h>
  37. #include <stdarg.h>
  38. #ifdef _WIN32
  39. #include <dbghelp.h>
  40. #include "modCheck.h"
  41. const int NumCodeBytes = 16; // Number of code bytes to record.
  42. const int MaxStackDump = 3072; // Maximum number of DWORDS in stack dumps.
  43. const int StackColumns = 4; // Number of columns in stack dump.
  44. #define ONEK 1024
  45. #define SIXTYFOURK (64*ONEK)
  46. #define ONEM (ONEK*ONEK)
  47. #define ONEG (ONEK*ONEK*ONEK)
  48. ///////////////////////////////////////////////////////////////////////////////
  49. // lstrrchr (avoid the C Runtime )
  50. static CHAR * lstrrchr(LPCSTR string, int ch)
  51. {
  52. CHAR *start = (CHAR *)string;
  53. while (*string++) /* find end of string */
  54. ;
  55. /* search towards front */
  56. while (--string != start && *string != (CHAR) ch)
  57. ;
  58. if (*string == (CHAR) ch) /* char found ? */
  59. return (CHAR *)string;
  60. return NULL;
  61. }
  62. ///////////////////////////////////////////////////////////////////////////////
  63. // hflush
  64. static void hflush(HANDLE LogFile)
  65. {
  66. if (toolkit_getResource()->hprintf_index > 0)
  67. {
  68. DWORD NumBytes;
  69. WriteFile(LogFile, toolkit_getResource()->hprintf_buffer, lstrlenA(toolkit_getResource()->hprintf_buffer), &NumBytes, 0);
  70. toolkit_getResource()->hprintf_index = 0;
  71. }
  72. }
  73. ///////////////////////////////////////////////////////////////////////////////
  74. // hprintf
  75. static void hprintf(HANDLE LogFile, LPCSTR Format, ...)
  76. {
  77. va_list arglist;
  78. if (toolkit_getResource()->hprintf_index > (HPRINTF_BUFFER_SIZE-1024))
  79. {
  80. DWORD NumBytes;
  81. WriteFile(LogFile, toolkit_getResource()->hprintf_buffer, lstrlenA(toolkit_getResource()->hprintf_buffer), &NumBytes, 0);
  82. toolkit_getResource()->hprintf_index = 0;
  83. }
  84. va_start( arglist, Format);
  85. toolkit_getResource()->hprintf_index += wvsprintfA(&(toolkit_getResource()->hprintf_buffer[toolkit_getResource()->hprintf_index]), Format, arglist);
  86. va_end( arglist);
  87. }
  88. ///////////////////////////////////////////////////////////////////////////////
  89. // FormatTime
  90. //
  91. // Format the specified FILETIME to output in a human readable format,
  92. // without using the C run time.
  93. static void FormatTime(LPSTR output, FILETIME TimeToPrint)
  94. {
  95. WORD Date, Time;
  96. output[0] = '\0';
  97. if (FileTimeToLocalFileTime(&TimeToPrint, &TimeToPrint) &&
  98. FileTimeToDosDateTime(&TimeToPrint, &Date, &Time))
  99. {
  100. wsprintfA(output, "%d/%d/%d %02d:%02d:%02d",
  101. (Date / 32) & 15, Date & 31, (Date / 512) + 1980,
  102. (Time >> 11), (Time >> 5) & 0x3F, (Time & 0x1F) * 2);
  103. }
  104. }
  105. ///////////////////////////////////////////////////////////////////////////////
  106. // DumpModuleInfo
  107. //
  108. // Print information about a code module (DLL or EXE) such as its size,
  109. // location, time stamp, etc.
  110. static BOOL DumpModuleInfo(HANDLE LogFile, HINSTANCE ModuleHandle, int nModuleNo)
  111. {
  112. BOOL rc = FALSE;
  113. CHAR szModName[MAX_PATH*2];
  114. ZeroMemory(szModName, sizeof(szModName));
  115. __try
  116. {
  117. if (GetModuleFileNameA(ModuleHandle, szModName, sizeof(szModName)-2) > 0)
  118. {
  119. IMAGE_NT_HEADERS *NTHeader;
  120. IMAGE_DOS_HEADER *DosHeader;
  121. HANDLE ModuleFile;
  122. CHAR TimeBuffer[100];
  123. DWORD FileSize = 0;
  124. // If GetModuleFileName returns greater than zero then this must
  125. // be a valid code module address. Therefore we can try to walk
  126. // our way through its structures to find the link time stamp.
  127. DosHeader = (IMAGE_DOS_HEADER*)ModuleHandle;
  128. if (IMAGE_DOS_SIGNATURE != DosHeader->e_magic)
  129. return FALSE;
  130. NTHeader = (IMAGE_NT_HEADERS*)((CHAR *)DosHeader
  131. + DosHeader->e_lfanew);
  132. if (IMAGE_NT_SIGNATURE != NTHeader->Signature)
  133. return FALSE;
  134. // open the code module file so that we can get its file date and size
  135. ModuleFile = CreateFileA(szModName, GENERIC_READ,
  136. FILE_SHARE_READ, 0, OPEN_EXISTING,
  137. FILE_ATTRIBUTE_NORMAL, 0);
  138. TimeBuffer[0] = '\0';
  139. if (ModuleFile != INVALID_HANDLE_VALUE)
  140. {
  141. FILETIME LastWriteTime;
  142. FileSize = GetFileSize(ModuleFile, 0);
  143. if (GetFileTime(ModuleFile, 0, 0, &LastWriteTime))
  144. {
  145. FormatTime(TimeBuffer, LastWriteTime);
  146. }
  147. CloseHandle(ModuleFile);
  148. }
  149. hprintf(LogFile, "Module %d\r\n", nModuleNo);
  150. hprintf(LogFile, "%s\r\n", szModName);
  151. hprintf(LogFile, "Image Base: 0x%08x Image Size: 0x%08x\r\n",
  152. NTHeader->OptionalHeader.ImageBase,
  153. NTHeader->OptionalHeader.SizeOfImage),
  154. hprintf(LogFile, "Checksum: 0x%08x Time Stamp: 0x%08x\r\n",
  155. NTHeader->OptionalHeader.CheckSum,
  156. NTHeader->FileHeader.TimeDateStamp);
  157. hprintf(LogFile, "File Size: %-10d File Time: %s\r\n",
  158. FileSize, TimeBuffer);
  159. hprintf(LogFile, "Version Information:\r\n");
  160. hprintf(LogFile, "\r\n");
  161. rc = TRUE;
  162. }
  163. }
  164. // Handle any exceptions by continuing from this point.
  165. __except(EXCEPTION_EXECUTE_HANDLER)
  166. {
  167. }
  168. return rc;
  169. }
  170. ///////////////////////////////////////////////////////////////////////////////
  171. // DumpModuleList
  172. //
  173. // Scan memory looking for code modules (DLLs or EXEs). VirtualQuery is used
  174. // to find all the blocks of address space that were reserved or committed,
  175. // and ShowModuleInfo will display module information if they are code
  176. // modules.
  177. static void DumpModuleList(HANDLE LogFile)
  178. {
  179. size_t pageNum;
  180. size_t NumPages;
  181. size_t PageSize;
  182. void *LastAllocationBase;
  183. SYSTEM_INFO SystemInfo;
  184. int nModuleNo;
  185. GetSystemInfo(&SystemInfo);
  186. PageSize = SystemInfo.dwPageSize;
  187. // Set NumPages to the number of pages in the 4GByte address space,
  188. // while being careful to avoid overflowing ints
  189. NumPages = 4 * ((size_t)(ONEG / PageSize));
  190. pageNum = 0;
  191. LastAllocationBase = 0;
  192. nModuleNo = 1;
  193. while (pageNum < NumPages)
  194. {
  195. MEMORY_BASIC_INFORMATION MemInfo;
  196. if (VirtualQuery((void *)(pageNum * PageSize), &MemInfo,
  197. sizeof(MemInfo)))
  198. {
  199. if (MemInfo.RegionSize > 0)
  200. {
  201. // Adjust the page number to skip over this block of memory
  202. pageNum += MemInfo.RegionSize / PageSize;
  203. if (MemInfo.State == MEM_COMMIT && MemInfo.AllocationBase >
  204. LastAllocationBase)
  205. {
  206. // Look for new blocks of committed memory, and try
  207. // recording their module names - this will fail
  208. // gracefully if they aren't code modules
  209. LastAllocationBase = MemInfo.AllocationBase;
  210. if (DumpModuleInfo(LogFile,
  211. (HINSTANCE)LastAllocationBase,
  212. nModuleNo))
  213. {
  214. nModuleNo++;
  215. }
  216. }
  217. }
  218. else
  219. pageNum += SIXTYFOURK / PageSize;
  220. }
  221. else
  222. pageNum += SIXTYFOURK / PageSize;
  223. // If VirtualQuery fails we advance by 64K because that is the
  224. // granularity of address space doled out by VirtualAlloc()
  225. }
  226. }
  227. ///////////////////////////////////////////////////////////////////////////////
  228. // DumpSystemInformation
  229. //
  230. // Record information about the user's system, such as processor type, amount
  231. // of memory, etc.
  232. static void DumpSystemInformation(HANDLE LogFile)
  233. {
  234. FILETIME CurrentTime;
  235. CHAR szTimeBuffer[100];
  236. CHAR szUserName[200];
  237. long crashID;
  238. DWORD UserNameSize;
  239. SYSTEM_INFO SystemInfo;
  240. CHAR szModuleName[MAX_PATH*2];
  241. MEMORYSTATUS MemInfo;;
  242. GetSystemTimeAsFileTime(&CurrentTime);
  243. FormatTime(szTimeBuffer, CurrentTime);
  244. crashID = CurrentTime.dwLowDateTime;
  245. ZeroMemory(toolkit_getResource()->szCrashID, sizeof(toolkit_getResource()->szCrashID));
  246. _ltoa_s( crashID, toolkit_getResource()->szCrashID, 20, 16 );
  247. hprintf(LogFile, "\r\nCRASH REFERENCE CODE: %s\r\n\r\n", toolkit_getResource()->szCrashID );
  248. hprintf(LogFile, "Error occurred at %s.\r\n", szTimeBuffer);
  249. ZeroMemory(szModuleName, sizeof(szModuleName));
  250. if (GetModuleFileNameA(0, szModuleName, _countof(szModuleName)-2) <= 0)
  251. lstrcpyA(szModuleName, "Unknown");
  252. ZeroMemory(szUserName, sizeof(szUserName));
  253. UserNameSize = _countof(szUserName)-2;
  254. if (!GetUserNameA(szUserName, &UserNameSize))
  255. lstrcpyA(szUserName, "Unknown");
  256. hprintf(LogFile, "%s, run by %s.\r\n", szModuleName, szUserName);
  257. GetSystemInfo(&SystemInfo);
  258. hprintf(LogFile, "%d processor(s), type %d.\r\n",
  259. SystemInfo.dwNumberOfProcessors, SystemInfo.dwProcessorType);
  260. MemInfo.dwLength = sizeof(MemInfo);
  261. GlobalMemoryStatus(&MemInfo);
  262. // Print out info on memory, rounded up.
  263. hprintf(LogFile, "%d%% memory in use.\r\n", MemInfo.dwMemoryLoad);
  264. hprintf(LogFile, "%d MBytes physical memory.\r\n", (MemInfo.dwTotalPhys +
  265. ONEM - 1) / ONEM);
  266. hprintf(LogFile, "%d MBytes physical memory free.\r\n",
  267. (MemInfo.dwAvailPhys + ONEM - 1) / ONEM);
  268. hprintf(LogFile, "%d MBytes paging file.\r\n", (MemInfo.dwTotalPageFile +
  269. ONEM - 1) / ONEM);
  270. hprintf(LogFile, "%d MBytes paging file free.\r\n",
  271. (MemInfo.dwAvailPageFile + ONEM - 1) / ONEM);
  272. hprintf(LogFile, "%d MBytes user address space.\r\n",
  273. (MemInfo.dwTotalVirtual + ONEM - 1) / ONEM);
  274. hprintf(LogFile, "%d MBytes user address space free.\r\n",
  275. (MemInfo.dwAvailVirtual + ONEM - 1) / ONEM);
  276. }
  277. ///////////////////////////////////////////////////////////////////////////////
  278. // GetExceptionDescription
  279. //
  280. // Translate the exception code into something human readable
  281. static const CHAR *GetExceptionDescription(DWORD ExceptionCode)
  282. {
  283. int i;
  284. struct ExceptionNames
  285. {
  286. DWORD ExceptionCode;
  287. CHAR * ExceptionName;
  288. };
  289. struct ExceptionNames ExceptionMap[] =
  290. {
  291. {0x40010005, "a Control-C"},
  292. {0x40010008, "a Control-Break"},
  293. {0x80000002, "a Datatype Misalignment"},
  294. {0x80000003, "a Breakpoint"},
  295. {0xc0000005, "an Access Violation"},
  296. {0xc0000006, "an In Page Error"},
  297. {0xc0000017, "a No Memory"},
  298. {0xc000001d, "an Illegal Instruction"},
  299. {0xc0000025, "a Noncontinuable Exception"},
  300. {0xc0000026, "an Invalid Disposition"},
  301. {0xc000008c, "a Array Bounds Exceeded"},
  302. {0xc000008d, "a Float Denormal Operand"},
  303. {0xc000008e, "a Float Divide by Zero"},
  304. {0xc000008f, "a Float Inexact Result"},
  305. {0xc0000090, "a Float Invalid Operation"},
  306. {0xc0000091, "a Float Overflow"},
  307. {0xc0000092, "a Float Stack Check"},
  308. {0xc0000093, "a Float Underflow"},
  309. {0xc0000094, "an Integer Divide by Zero"},
  310. {0xc0000095, "an Integer Overflow"},
  311. {0xc0000096, "a Privileged Instruction"},
  312. {0xc00000fD, "a Stack Overflow"},
  313. {0xc0000142, "a DLL Initialization Failed"},
  314. {0xe06d7363, "a Microsoft C++ Exception"},
  315. };
  316. for (i = 0; i < sizeof(ExceptionMap) / sizeof(ExceptionMap[0]); i++)
  317. if (ExceptionCode == ExceptionMap[i].ExceptionCode)
  318. return ExceptionMap[i].ExceptionName;
  319. return "an Unknown exception type";
  320. }
  321. ///////////////////////////////////////////////////////////////////////////////
  322. // GetFilePart
  323. static CHAR * GetFilePart(LPCSTR source)
  324. {
  325. CHAR *result = lstrrchr(source, '\\');
  326. if (result)
  327. result++;
  328. else
  329. result = (CHAR *)source;
  330. return result;
  331. }
  332. ///////////////////////////////////////////////////////////////////////////////
  333. // DumpStack
  334. static void DumpStack(HANDLE LogFile, DWORD *pStack)
  335. {
  336. hprintf(LogFile, "\r\n\r\nStack:\r\n");
  337. __try
  338. {
  339. // Esp contains the bottom of the stack, or at least the bottom of
  340. // the currently used area.
  341. DWORD* pStackTop;
  342. DWORD* pStackStart;
  343. int Count;
  344. int nDwordsPrinted;
  345. __asm
  346. {
  347. // Load the top (highest address) of the stack from the
  348. // thread information block. It will be found there in
  349. // Win9x and Windows NT.
  350. mov eax, fs:[4]
  351. mov pStackTop, eax
  352. }
  353. if (pStackTop > pStack + MaxStackDump)
  354. pStackTop = pStack + MaxStackDump;
  355. Count = 0;
  356. pStackStart = pStack;
  357. nDwordsPrinted = 0;
  358. while (pStack + 1 <= pStackTop)
  359. {
  360. if ((Count % StackColumns) == 0)
  361. {
  362. pStackStart = pStack;
  363. nDwordsPrinted = 0;
  364. hprintf(LogFile, "0x%08x: ", pStack);
  365. }
  366. if ((++Count % StackColumns) == 0 || pStack + 2 > pStackTop)
  367. {
  368. int n, i;
  369. hprintf(LogFile, "%08x ", *pStack);
  370. nDwordsPrinted++;
  371. n = nDwordsPrinted;
  372. while (n < 4)
  373. {
  374. hprintf(LogFile, " ");
  375. n++;
  376. }
  377. for (i = 0; i < nDwordsPrinted; i++)
  378. {
  379. int j;
  380. DWORD dwStack = *pStackStart;
  381. for (j = 0; j < 4; j++)
  382. {
  383. char c = (char)(dwStack & 0xFF);
  384. if (c < 0x20 || c > 0x7E)
  385. c = '.';
  386. hprintf(LogFile, "%c", c);
  387. dwStack = dwStack >> 8;
  388. }
  389. pStackStart++;
  390. }
  391. hprintf(LogFile, "\r\n");
  392. }
  393. else
  394. {
  395. hprintf(LogFile, "%08x ", *pStack);
  396. nDwordsPrinted++;
  397. }
  398. pStack++;
  399. }
  400. hprintf(LogFile, "\r\n");
  401. }
  402. __except(EXCEPTION_EXECUTE_HANDLER)
  403. {
  404. hprintf(LogFile, "Exception encountered during stack dump.\r\n");
  405. }
  406. }
  407. ///////////////////////////////////////////////////////////////////////////////
  408. // DumpRegisters
  409. static void DumpRegisters(HANDLE LogFile, PCONTEXT Context)
  410. {
  411. // Print out the register values in an XP error window compatible format.
  412. hprintf(LogFile, "\r\n");
  413. hprintf(LogFile, "Context:\r\n");
  414. hprintf(LogFile, "EDI: 0x%08x ESI: 0x%08x EAX: 0x%08x\r\n",
  415. Context->Edi, Context->Esi, Context->Eax);
  416. hprintf(LogFile, "EBX: 0x%08x ECX: 0x%08x EDX: 0x%08x\r\n",
  417. Context->Ebx, Context->Ecx, Context->Edx);
  418. hprintf(LogFile, "EIP: 0x%08x EBP: 0x%08x SegCs: 0x%08x\r\n",
  419. Context->Eip, Context->Ebp, Context->SegCs);
  420. hprintf(LogFile, "EFlags: 0x%08x ESP: 0x%08x SegSs: 0x%08x\r\n",
  421. Context->EFlags, Context->Esp, Context->SegSs);
  422. }
  423. #endif
  424. ///////////////////////////////////////////////////////////////////////////////
  425. ///////////////////////////////////////////////////////////////////////////////
  426. //
  427. // RecordExceptionInfo
  428. //
  429. ///////////////////////////////////////////////////////////////////////////////
  430. ///////////////////////////////////////////////////////////////////////////////
  431. TOOLKIT_API int TOOLKIT_CC DumpExceptionInfo(PEXCEPTION_POINTERS pExceptPtrs, HANDLE hLogFile)
  432. {
  433. #ifdef _WIN32
  434. PEXCEPTION_RECORD Exception = pExceptPtrs->ExceptionRecord;
  435. PCONTEXT Context = pExceptPtrs->ContextRecord;
  436. CHAR *pszCrashModuleFileName;
  437. CHAR szCrashModulePathName[MAX_PATH*2];
  438. MEMORY_BASIC_INFORMATION MemInfo;
  439. BYTE * code;
  440. int codebyte;
  441. DWORD* pStack;
  442. ZeroMemory(szCrashModulePathName, sizeof(szCrashModulePathName));
  443. pszCrashModuleFileName = "Unknown";
  444. // VirtualQuery can be used to get the allocation base associated with a
  445. // code address, which is the same as the ModuleHandle. This can be used
  446. // to get the filename of the module that the crash happened in.
  447. if (VirtualQuery((void*)Context->Eip, &MemInfo, sizeof(MemInfo)) &&
  448. (GetModuleFileNameA((HINSTANCE)MemInfo.AllocationBase,
  449. szCrashModulePathName,
  450. sizeof(szCrashModulePathName)-2) > 0))
  451. {
  452. pszCrashModuleFileName = GetFilePart(szCrashModulePathName);
  453. }
  454. // Print out the beginning of the error log in a Win95 error window
  455. // compatible format.
  456. hprintf(hLogFile, "caused %s (0x%08x) \r\nin module %s at %04x:%08x.\r\n\r\n",
  457. GetExceptionDescription(Exception->ExceptionCode),
  458. Exception->ExceptionCode,
  459. pszCrashModuleFileName, Context->SegCs, Context->Eip);
  460. hprintf(hLogFile, "Exception handler called.\r\n");
  461. DumpSystemInformation(hLogFile);
  462. // If the exception was an access violation, print out some additional
  463. // information, to the error log and the debugger.
  464. if (Exception->ExceptionCode == STATUS_ACCESS_VIOLATION &&
  465. Exception->NumberParameters >= 2)
  466. {
  467. CHAR szDebugMessage[1000];
  468. const CHAR* readwrite = "Read from";
  469. if (Exception->ExceptionInformation[0])
  470. readwrite = "Write to";
  471. wsprintfA(szDebugMessage, "%s location %08x caused an access violation.\r\n",
  472. readwrite, Exception->ExceptionInformation[1]);
  473. hprintf(hLogFile, "%s", szDebugMessage);
  474. }
  475. DumpRegisters(hLogFile, Context);
  476. // Print out the bytes of code at the instruction pointer. Since the
  477. // crash may have been caused by an instruction pointer that was bad,
  478. // this code needs to be wrapped in an exception handler, in case there
  479. // is no memory to read. If the dereferencing of code[] fails, the
  480. // exception handler will print '??'.
  481. hprintf(hLogFile, "\r\nBytes at CS:EIP:\r\n");
  482. code = (BYTE *)Context->Eip;
  483. for (codebyte = 0; codebyte < NumCodeBytes; codebyte++)
  484. {
  485. __try
  486. {
  487. hprintf(hLogFile, "%02x ", code[codebyte]);
  488. }
  489. __except(EXCEPTION_EXECUTE_HANDLER)
  490. {
  491. hprintf(hLogFile, "?? ");
  492. }
  493. }
  494. // Time to print part or all of the stack to the error log. This allows
  495. // us to figure out the call stack, parameters, local variables, etc.
  496. // Esp contains the bottom of the stack, or at least the bottom of
  497. // the currently used area
  498. pStack = (DWORD *)Context->Esp;
  499. DumpStack(hLogFile, pStack);
  500. DumpModuleList(hLogFile);
  501. hflush(hLogFile);
  502. #endif //_WIN32
  503. return EXCEPTION_EXECUTE_HANDLER;
  504. }