]> git.saurik.com Git - wxWidgets.git/blame - src/msw/crashrpt.cpp
fixed Xcode 1.2 warnings; code cleanup
[wxWidgets.git] / src / msw / crashrpt.cpp
CommitLineData
50bea100
VZ
1/////////////////////////////////////////////////////////////////////////////
2// Name: msw/crashrpt.cpp
3// Purpose: helpers for structured exception handling (SEH)
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 13.07.03
7// RCS-ID: $Id$
8// Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12/*
2bbc1b28
VZ
13 The code generating the crash reports in this file is heavily based on
14 Matt Pietrek's column from the March 2002 issue of MSDN Magazine. Note
15 that this code is not currently used by default, however. In any case,
16 all bugs are my alone.
50bea100
VZ
17 */
18
19// ============================================================================
20// declarations
21// ============================================================================
22
23// ----------------------------------------------------------------------------
24// headers
25// ----------------------------------------------------------------------------
26
27// For compilers that support precompilation, includes "wx.h".
28#include "wx/wxprec.h"
29
30#ifdef __BORLANDC__
31 #pragma hdrstop
32#endif
33
34#if wxUSE_ON_FATAL_EXCEPTION
35
36#ifndef WX_PRECOMP
37#endif //WX_PRECOMP
38
2bbc1b28
VZ
39/*
40 We have two possibilities here: one, a priori more interesting, is to
41 generate the crash report ourselves and include the values of all the
42 variables in the dump. Unfortunately my code to do it doesn't work in
43 "real life" situations i.e. it works in small examples but invariably
44 gets confused by something in big programs which makes quite useless.
45
46 The other possibility is to let dbghelp.dll to do the work for us and
47 analyze its results later using a debugger with knowledge about crash
48 dumps, such as (free!) WinDbg. This also has another advantage of not
49 needing to ship the .pdb file (containing debug info) to the user. So
50 this is the default now, but I keep the old code just in case, and if
51 you really want you can still use it.
52 */
53#define wxUSE_MINIDUMP 1
54
55#if !wxUSE_MINIDUMP
56 #include "wx/longlong.h"
57#endif // wxUSE_MINIDUMP
58
50bea100 59#include "wx/datetime.h"
2bbc1b28 60
50bea100
VZ
61#include "wx/dynload.h"
62
63#include "wx/msw/crashrpt.h"
64
9ed0d735 65#include "wx/msw/wrapwin.h"
50bea100
VZ
66#include <imagehlp.h>
67#include "wx/msw/private.h"
68
0b7824d7
VZ
69// we need to determine whether we have the declarations for the function in
70// debughlp.dll version 5.81 (at least) and we check for DBHLPAPI to test this
71//
72// reasons:
73// - VC6 version of imagehlp.h doesn't define it
74// - VC7 one does
75// - testing for compiler version doesn't work as you can install and use
76// the new SDK headers with VC6
77//
78// in any case, the user may override by defining wxUSE_DBGHELP himself
79#ifndef wxUSE_DBGHELP
8d008965 80 #ifdef DBHLPAPI
0b7824d7
VZ
81 #define wxUSE_DBGHELP 1
82 #else
83 #define wxUSE_DBGHELP 0
84 #endif
85#endif
86
87#if wxUSE_DBGHELP
88
50bea100
VZ
89// ----------------------------------------------------------------------------
90// types of imagehlp.h functions
91// ----------------------------------------------------------------------------
92
2bbc1b28
VZ
93#if wxUSE_MINIDUMP
94
95typedef BOOL (WINAPI *MiniDumpWriteDump_t)(HANDLE, DWORD, HANDLE,
96 MINIDUMP_TYPE,
97 CONST PMINIDUMP_EXCEPTION_INFORMATION,
98 CONST PMINIDUMP_USER_STREAM_INFORMATION,
99 CONST PMINIDUMP_CALLBACK_INFORMATION);
100#else // !wxUSE_MINIDUMP
50bea100
VZ
101typedef DWORD (WINAPI *SymSetOptions_t)(DWORD);
102typedef BOOL (WINAPI *SymInitialize_t)(HANDLE, LPSTR, BOOL);
103typedef BOOL (WINAPI *StackWalk_t)(DWORD, HANDLE, HANDLE, LPSTACKFRAME,
104 LPVOID, PREAD_PROCESS_MEMORY_ROUTINE,
105 PFUNCTION_TABLE_ACCESS_ROUTINE,
106 PGET_MODULE_BASE_ROUTINE,
107 PTRANSLATE_ADDRESS_ROUTINE);
108typedef BOOL (WINAPI *SymFromAddr_t)(HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO);
109typedef LPVOID (WINAPI *SymFunctionTableAccess_t)(HANDLE, DWORD);
110typedef DWORD (WINAPI *SymGetModuleBase_t)(HANDLE, DWORD);
111typedef BOOL (WINAPI *SymGetLineFromAddr_t)(HANDLE, DWORD,
112 PDWORD, PIMAGEHLP_LINE);
113typedef BOOL (WINAPI *SymSetContext_t)(HANDLE, PIMAGEHLP_STACK_FRAME,
114 PIMAGEHLP_CONTEXT);
115typedef BOOL (WINAPI *SymEnumSymbols_t)(HANDLE, ULONG64, PCSTR,
116 PSYM_ENUMERATESYMBOLS_CALLBACK, PVOID);
117typedef BOOL (WINAPI *SymGetTypeInfo_t)(HANDLE, DWORD64, ULONG,
118 IMAGEHLP_SYMBOL_TYPE_INFO, PVOID);
2bbc1b28 119#endif // wxUSE_MINIDUMP
50bea100
VZ
120
121// ----------------------------------------------------------------------------
2bbc1b28 122// constants
50bea100
VZ
123// ----------------------------------------------------------------------------
124
2bbc1b28
VZ
125#if !wxUSE_MINIDUMP
126
50bea100
VZ
127// Stolen from CVCONST.H in the DIA 2.0 SDK
128enum BasicType
129{
130 BASICTYPE_NOTYPE = 0,
131 BASICTYPE_VOID = 1,
132 BASICTYPE_CHAR = 2,
133 BASICTYPE_WCHAR = 3,
134 BASICTYPE_INT = 6,
135 BASICTYPE_UINT = 7,
136 BASICTYPE_FLOAT = 8,
137 BASICTYPE_BCD = 9,
138 BASICTYPE_BOOL = 10,
139 BASICTYPE_LONG = 13,
140 BASICTYPE_ULONG = 14,
141 BASICTYPE_CURRENCY = 25,
142 BASICTYPE_DATE = 26,
143 BASICTYPE_VARIANT = 27,
144 BASICTYPE_COMPLEX = 28,
145 BASICTYPE_BIT = 29,
146 BASICTYPE_BSTR = 30,
147 BASICTYPE_HRESULT = 31
148};
149
150// Same as above
151enum SymbolTag
152{
153 SYMBOL_TAG_NULL,
154 SYMBOL_TAG_FUNCTION = 5,
155 SYMBOL_TAG_DATA = 7,
156 SYMBOL_TAG_PUBLIC = 10, // appears in .DBGs
157 SYMBOL_TAG_UDT,
158 SYMBOL_TAG_ENUM,
159 SYMBOL_TAG_FUNCTION_TYPE,
160 SYMBOL_TAG_POINTER_TYPE,
161 SYMBOL_TAG_ARRAY_TYPE,
162 SYMBOL_TAG_BASE_TYPE,
163 SYMBOL_TAG_TYPEDEF,
164 SYMBOL_TAG_BASECLASS
165};
166
2bbc1b28
VZ
167#endif // wxUSE_MINIDUMP
168
0b7824d7
VZ
169#endif // wxUSE_DBGHELP
170
50bea100
VZ
171// ----------------------------------------------------------------------------
172// classes
173// ----------------------------------------------------------------------------
174
8d008965
VZ
175// low level wxBusyCursor replacement: we use Win32 API directly here instead
176// of going through wxWindows calls as this could be dangerous
177class BusyCursor
178{
179public:
180 BusyCursor()
181 {
182 HCURSOR hcursorBusy = ::LoadCursor(NULL, IDC_WAIT);
183 m_hcursorOld = ::SetCursor(hcursorBusy);
184 }
185
186 ~BusyCursor()
187 {
188 if ( m_hcursorOld )
189 {
190 ::SetCursor(m_hcursorOld);
191 }
192 }
193
194private:
195 HCURSOR m_hcursorOld;
196};
197
50bea100
VZ
198// the real crash report generator
199class wxCrashReportImpl
200{
201public:
202 wxCrashReportImpl(const wxChar *filename);
203
204 bool Generate(int flags);
205
206 ~wxCrashReportImpl()
207 {
208 if ( m_hFile != INVALID_HANDLE_VALUE )
209 {
210 ::CloseHandle(m_hFile);
211 }
212 }
213
214private:
2bbc1b28 215
50bea100
VZ
216 // formatted output to m_hFile
217 void Output(const wxChar *format, ...);
218
219 // output end of line
220 void OutputEndl() { Output(_T("\r\n")); }
221
0b7824d7 222#if wxUSE_DBGHELP
2bbc1b28
VZ
223
224#if !wxUSE_MINIDUMP
50bea100
VZ
225 // translate exception code to its symbolic name
226 static wxString GetExceptionString(DWORD dwCode);
227
228 // return the type from "type index"
229 static BasicType GetBasicType(DWORD64 modBase, DWORD typeIndex);
230
231 // return the name for the type index
232 static wxString GetSymbolName(DWORD64 modBase, DWORD dwTypeIndex);
233
234 // return the string representation of the variable value
235 static wxString FormatSimpleValue(BasicType bt,
236 DWORD64 length,
237 PVOID pAddress);
238
239 // return string representation of a struct field (which may itself be a
240 // struct, of course)
241 static wxString FormatField(DWORD64 modBase,
242 DWORD dwTypeIndex,
243 void *pVariable,
244 unsigned level);
245
246 // show the name and value of the given symbol
247 static wxString FormatSymbol(PSYMBOL_INFO pSym, STACKFRAME *sf);
248
249 // show value described by SYMBOL_INFO located at pVariable
250 static wxString FormatAnyValue(PSYMBOL_INFO pSym, void *pVariable);
251
252 // show value of possibly complex (user-defined) type
253 static wxString FormatUDT(DWORD64 modBase,
254 DWORD dwTypeIndex,
255 void *pVariable,
256 unsigned level = 0);
257
258 // outputs information about the given symbol
259 void OutputSymbol(PSYMBOL_INFO pSymInfo, STACKFRAME *sf);
260
50bea100
VZ
261 // map address to module (and also section:offset), retunr true if ok
262 static bool GetLogicalAddress(PVOID addr,
263 PTSTR szModule,
264 DWORD len,
265 DWORD& section,
266 DWORD& offset);
267
268 // callback used with SymEnumSymbols() to process all variables
269 static BOOL CALLBACK EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo,
270 ULONG SymbolSize,
271 PVOID UserContext);
272
273
274 // show the general information about exception which should be always
275 // available
276 //
277 // returns the module of the handle where the crash occured
278 HANDLE OutputBasicContext(EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pCtx);
279
280 // output the call stack and local variables values
281 void OutputStack(const CONTEXT *pCtx, int flags);
282
283 // output the global variables values
284 void OutputGlobals(HANDLE hModuleCrash);
285
286
50bea100
VZ
287 // the current stack frame (may be NULL)
288 STACKFRAME *m_sfCurrent;
2bbc1b28
VZ
289#endif // !wxUSE_MINIDUMP
290
291 // load all the functions we need from dbghelp.dll, return true if all ok
292 bool BindDbgHelpFunctions(const wxDynamicLibrary& dllDbgHelp);
50bea100
VZ
293
294
295 // dynamically loaded dbghelp.dll functions
296 #define DECLARE_SYM_FUNCTION(func) static func ## _t func
297
2bbc1b28
VZ
298#if wxUSE_MINIDUMP
299 DECLARE_SYM_FUNCTION(MiniDumpWriteDump);
300#else // !wxUSE_MINIDUMP
50bea100
VZ
301 DECLARE_SYM_FUNCTION(SymSetOptions);
302 DECLARE_SYM_FUNCTION(SymInitialize);
303 DECLARE_SYM_FUNCTION(StackWalk);
304 DECLARE_SYM_FUNCTION(SymFromAddr);
305 DECLARE_SYM_FUNCTION(SymFunctionTableAccess);
306 DECLARE_SYM_FUNCTION(SymGetModuleBase);
307 DECLARE_SYM_FUNCTION(SymGetLineFromAddr);
308 DECLARE_SYM_FUNCTION(SymSetContext);
309 DECLARE_SYM_FUNCTION(SymEnumSymbols);
310 DECLARE_SYM_FUNCTION(SymGetTypeInfo);
2bbc1b28
VZ
311#endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP
312
313 #undef DECLARE_SYM_FUNCTION
0b7824d7 314#endif // wxUSE_DBGHELP
f83aa777
VZ
315
316 // the handle of the report file
317 HANDLE m_hFile;
50bea100
VZ
318};
319
320// ----------------------------------------------------------------------------
321// globals
322// ----------------------------------------------------------------------------
323
324// global pointer to exception information, only valid inside OnFatalException
325extern WXDLLIMPEXP_BASE EXCEPTION_POINTERS *wxGlobalSEInformation = NULL;
326
327
328// flag telling us whether the application wants to handle exceptions at all
329static bool gs_handleExceptions = false;
330
331// the file name where the report about exception is written
332static wxChar gs_reportFilename[MAX_PATH];
333
334// ============================================================================
335// implementation
336// ============================================================================
337
0b7824d7
VZ
338#if wxUSE_DBGHELP
339
50bea100
VZ
340#define DEFINE_SYM_FUNCTION(func) func ## _t wxCrashReportImpl::func = 0
341
2bbc1b28
VZ
342#if wxUSE_MINIDUMP
343DEFINE_SYM_FUNCTION(MiniDumpWriteDump);
344#else // !wxUSE_MINIDUMP
50bea100
VZ
345DEFINE_SYM_FUNCTION(SymSetOptions);
346DEFINE_SYM_FUNCTION(SymInitialize);
347DEFINE_SYM_FUNCTION(StackWalk);
348DEFINE_SYM_FUNCTION(SymFromAddr);
349DEFINE_SYM_FUNCTION(SymFunctionTableAccess);
350DEFINE_SYM_FUNCTION(SymGetModuleBase);
351DEFINE_SYM_FUNCTION(SymGetLineFromAddr);
352DEFINE_SYM_FUNCTION(SymSetContext);
353DEFINE_SYM_FUNCTION(SymEnumSymbols);
354DEFINE_SYM_FUNCTION(SymGetTypeInfo);
2bbc1b28 355#endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP
50bea100
VZ
356
357#undef DEFINE_SYM_FUNCTION
358
0b7824d7
VZ
359#endif // wxUSE_DBGHELP
360
50bea100
VZ
361// ----------------------------------------------------------------------------
362// wxCrashReportImpl
363// ----------------------------------------------------------------------------
364
365wxCrashReportImpl::wxCrashReportImpl(const wxChar *filename)
366{
2bbc1b28 367#if wxUSE_DBGHELP && !wxUSE_MINIDUMP
50bea100 368 m_sfCurrent = NULL;
0b7824d7 369#endif // wxUSE_DBGHELP
50bea100
VZ
370
371 m_hFile = ::CreateFile
372 (
373 filename,
374 GENERIC_WRITE,
375 0, // no sharing
376 NULL, // default security
377 CREATE_ALWAYS,
378 FILE_FLAG_WRITE_THROUGH,
379 NULL // no template file
380 );
381}
382
383void wxCrashReportImpl::Output(const wxChar *format, ...)
384{
385 va_list argptr;
386 va_start(argptr, format);
387
388 DWORD cbWritten;
389
390 wxString s = wxString::FormatV(format, argptr);
391 ::WriteFile(m_hFile, s, s.length() * sizeof(wxChar), &cbWritten, 0);
392
393 va_end(argptr);
394}
395
0b7824d7
VZ
396#if wxUSE_DBGHELP
397
2bbc1b28
VZ
398#if !wxUSE_MINIDUMP
399
50bea100
VZ
400bool
401wxCrashReportImpl::GetLogicalAddress(PVOID addr,
402 PTSTR szModule,
403 DWORD len,
404 DWORD& section,
405 DWORD& offset)
406{
407 MEMORY_BASIC_INFORMATION mbi;
408
409 if ( !::VirtualQuery(addr, &mbi, sizeof(mbi)) )
410 return false;
411
412 DWORD hMod = (DWORD)mbi.AllocationBase;
413
414 if ( !::GetModuleFileName((HMODULE)hMod, szModule, len) )
415 return false;
416
417 // Point to the DOS header in memory
418 PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
419
420 // From the DOS header, find the NT (PE) header
421 PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + pDosHdr->e_lfanew);
422
423 PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr );
424
425 DWORD rva = (DWORD)addr - hMod; // RVA is offset from module load address
426
427 // Iterate through the section table, looking for the one that encompasses
428 // the linear address.
429 const DWORD nSections = pNtHdr->FileHeader.NumberOfSections;
430 for ( DWORD i = 0; i < nSections; i++, pSection++ )
431 {
432 DWORD sectionStart = pSection->VirtualAddress;
433 DWORD sectionEnd = sectionStart
434 + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);
435
436 // Is the address in this section?
437 if ( (rva >= sectionStart) && (rva <= sectionEnd) )
438 {
439 // Yes, address is in the section. Calculate section and offset,
440 // and store in the "section" & "offset" params, which were
441 // passed by reference.
442 section = i + 1;
443 offset = rva - sectionStart;
444
445 return true;
446 }
447 }
448
449 // failed to map to logical address...
450 return false;
451}
452
453/* static */ BasicType
454wxCrashReportImpl::GetBasicType(DWORD64 modBase, DWORD typeIndex)
455{
456 const HANDLE hProcess = GetCurrentProcess();
457
458 // try the index we have
459 BasicType bt;
460 if ( SymGetTypeInfo(hProcess, modBase, typeIndex, TI_GET_BASETYPE, &bt) )
461 {
462 return bt;
463 }
464
465 // if failed, try to get the "real" typeid first
466 DWORD typeId;
467 if ( SymGetTypeInfo(hProcess, modBase, typeIndex, TI_GET_TYPEID, &typeId)
468 &&
469 (typeId != typeIndex &&
470 SymGetTypeInfo(hProcess, modBase, typeId, TI_GET_BASETYPE, &bt)) )
471 {
472 return bt;
473 }
474
475 return BASICTYPE_NOTYPE;
476}
477
478/* static */ wxString
479wxCrashReportImpl::FormatSimpleValue(BasicType bt,
480 DWORD64 length,
481 PVOID pAddress)
482{
483 wxString s;
484 s.reserve(256);
485
486 // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
487 if ( length == 1 )
488 {
489 s.Printf(_T("%#04x"), *(PBYTE)pAddress);
490 }
491 else if ( length == 2 )
492 {
493 s.Printf(_T("%#06x"), *(PWORD)pAddress);
494 }
495 else if ( length == 4 )
496 {
497 bool handled = false;
498
499 if ( bt == BASICTYPE_FLOAT )
500 {
501 s.Printf(_T("%f"), *(PFLOAT)pAddress);
502
503 handled = true;
504 }
505 else if ( bt == BASICTYPE_CHAR )
506 {
507 static const size_t NUM_CHARS = 32;
508
87baeeaf
VZ
509 const char * const pc = *(PSTR *)pAddress;
510 if ( !::IsBadStringPtrA(pc, NUM_CHARS) )
50bea100 511 {
87baeeaf 512 s << _T('"') << wxString(pc, wxConvLibc, NUM_CHARS) << _T('"');
50bea100
VZ
513
514 handled = true;
515 }
516 }
517
518 if ( !handled )
519 {
520 // treat just as an opaque DWORD
521 s.Printf(_T("%#x"), *(PDWORD)pAddress);
522 }
523 }
524 else if ( length == 8 )
525 {
526 if ( bt == BASICTYPE_FLOAT )
527 {
528 s.Printf(_T("%lf"), *(double *)pAddress);
529 }
530 else // opaque 64 bit value
531 {
532 s.Printf(_T("%#" wxLongLongFmtSpec _T("x")), *(PDWORD *)pAddress);
533 }
534 }
535
536 return s;
537}
538
539/* static */
540wxString wxCrashReportImpl::GetSymbolName(DWORD64 modBase, DWORD dwTypeIndex)
541{
542 wxString s;
543
544 WCHAR *pwszTypeName;
545 if ( SymGetTypeInfo
546 (
547 GetCurrentProcess(),
548 modBase,
549 dwTypeIndex,
550 TI_GET_SYMNAME,
551 &pwszTypeName
552 ) )
553 {
554 s = wxConvCurrent->cWC2WX(pwszTypeName);
555
556 ::LocalFree(pwszTypeName);
557 }
558
559 return s;
560}
561
562// this is called for the struct members/base classes
563wxString
564wxCrashReportImpl::FormatField(DWORD64 modBase,
565 DWORD dwTypeIndex,
566 void *pVariable,
567 unsigned level)
568{
569 wxString s;
570
fc943dca
VZ
571 // avoid infinite recursion
572 if ( level > 10 )
573 {
574 return s;
575 }
576
50bea100
VZ
577 const HANDLE hProcess = GetCurrentProcess();
578
579 DWORD dwTag = 0;
580 SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_GET_SYMTAG, &dwTag);
581
582 switch ( dwTag )
583 {
584 case SYMBOL_TAG_UDT:
585 case SYMBOL_TAG_BASECLASS:
586 s = FormatUDT(modBase, dwTypeIndex, pVariable, level);
587 break;
588
589 case SYMBOL_TAG_FUNCTION:
590 // don't show
591 break;
592
593 default:
594 // try to treat all the rest as data even though it's not clear if
595 // it's really a good idea...
596
597 // Get the offset of the child member, relative to its parent
598 DWORD dwMemberOffset = 0;
599 SymGetTypeInfo(hProcess, modBase, dwTypeIndex,
600 TI_GET_OFFSET, &dwMemberOffset);
601
602 // Get the real "TypeId" of the child. We need this for the
603 // SymGetTypeInfo(TI_GET_LENGTH) call below.
604 DWORD typeId;
605 if ( !SymGetTypeInfo(hProcess, modBase, dwTypeIndex,
606 TI_GET_TYPEID, &typeId) )
607 {
608 typeId = dwTypeIndex;
609 }
610
611 // Get the size of the child member
612 ULONG64 size;
613 SymGetTypeInfo(hProcess, modBase, typeId, TI_GET_LENGTH, &size);
614
615 // Calculate the address of the member
616 DWORD_PTR dwFinalOffset = (DWORD_PTR)pVariable + dwMemberOffset;
617
618 BasicType basicType = GetBasicType(modBase, dwTypeIndex);
619
620 s = FormatSimpleValue(basicType, size, (PVOID)dwFinalOffset);
621 break;
622
623 }
624
625 if ( s.empty() )
626 {
627 // don't show if no value -- what for?
628 return s;
629 }
630
631 return wxString(_T('\t'), level + 1) +
632 GetSymbolName(modBase, dwTypeIndex) +
633 _T(" = ") + s + _T("\r\n");
634}
635
636// If it's a user defined type (UDT), recurse through its members until we're
637// at fundamental types.
638wxString
639wxCrashReportImpl::FormatUDT(DWORD64 modBase,
640 DWORD dwTypeIndex,
641 void *pVariable,
642 unsigned level)
643{
644 wxString s;
645 s.reserve(512);
646 s = GetSymbolName(modBase, dwTypeIndex) + _T(" {\r\n");
647
648 const HANDLE hProcess = GetCurrentProcess();
649
650 // Determine how many children this type has.
651 DWORD dwChildrenCount = 0;
652 SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT,
653 &dwChildrenCount);
654
655 // Prepare to get an array of "TypeIds", representing each of the children.
656 TI_FINDCHILDREN_PARAMS *children = (TI_FINDCHILDREN_PARAMS *)
657 malloc(sizeof(TI_FINDCHILDREN_PARAMS) +
658 (dwChildrenCount - 1)*sizeof(ULONG));
659 if ( !children )
660 return s;
661
662 children->Count = dwChildrenCount;
663 children->Start = 0;
664
665 // Get the array of TypeIds, one for each child type
666 if ( !SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN,
667 children) )
668 {
669 return s;
670 }
671
672 // Iterate through all children
673 for ( unsigned i = 0; i < dwChildrenCount; i++ )
674 {
675 s += FormatField(modBase, children->ChildId[i], pVariable, level + 1);
676 }
677
678 free(children);
679
680 s << wxString(_T('\t'), level + 1) << _T('}');
681
682 return s;
683}
684
685// return the string containing the symbol of the given symbol
686/* static */ wxString
687wxCrashReportImpl::FormatAnyValue(PSYMBOL_INFO pSym, void *pVariable)
688{
689 DWORD dwTag = 0;
690 SymGetTypeInfo(GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
691 TI_GET_SYMTAG, &dwTag);
692
693 wxString s;
694 switch ( dwTag )
695 {
696 case SYMBOL_TAG_FUNCTION:
697 break;
698
699 case SYMBOL_TAG_UDT:
700 case SYMBOL_TAG_BASECLASS:
701 // show UDT recursively
702 s = FormatUDT(pSym->ModBase, pSym->TypeIndex, pVariable);
703 break;
704
705 default:
706 // variable of simple type (but could be array which we don't
707 // handle correctly yet...), format it using its type and size
708 BasicType bt = GetBasicType(pSym->ModBase, pSym->TypeIndex);
709
710 s = FormatSimpleValue(bt, pSym->Size, pVariable);
711 break;
712
713 }
714
715 return s;
716}
717
718// display contents and type of the given variable
719/* static */ wxString
720wxCrashReportImpl::FormatSymbol(PSYMBOL_INFO pSym, STACKFRAME *sf)
721{
722 wxString s;
723
724 if ( pSym->Tag == SYMBOL_TAG_FUNCTION )
725 {
726 // If it's a function, don't do anything.
727 return s;
728 }
729
730 if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER )
731 {
732 // Don't try to report register variable
733 return s;
734 }
735
736 s.reserve(512);
737
738 // Indicate if the variable is a local or parameter
739 if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER )
740 s += _T("\t[param] ");
741 else if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL )
742 s += _T("\t[local] ");
743
744 // Will point to the variable's data in memory
745 DWORD_PTR pVariable = 0;
746
747 if ( (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE) && sf )
748 {
749 pVariable = sf->AddrFrame.Offset;
750 pVariable += (DWORD_PTR)pSym->Address;
751 }
752 else // It must be a global variable
753 {
754 pVariable = (DWORD_PTR)pSym->Address;
755 }
756
87baeeaf
VZ
757 s << wxString(pSym->Name, wxConvLibc)
758 << _T(" = ")
759 << FormatAnyValue(pSym, (PVOID)pVariable);
50bea100
VZ
760
761 return s;
762}
763
764void
765wxCrashReportImpl::OutputSymbol(PSYMBOL_INFO pSymInfo, STACKFRAME *sf)
766{
767 wxString s = FormatSymbol(pSymInfo, sf);
768 if ( !s.empty() )
769 {
770 Output(_T("%s\r\n"), s.c_str());
771 }
772 //else: not an interesting symbol
773}
774
775// callback for SymEnumSymbols()
776/* static */
777BOOL CALLBACK
778wxCrashReportImpl::EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo,
779 ULONG WXUNUSED(SymbolSize),
780 PVOID UserContext)
781{
782 wxCrashReportImpl *self = (wxCrashReportImpl *)UserContext;
783
784 __try
785 {
786 self->OutputSymbol(pSymInfo, self->m_sfCurrent);
787 }
788 __except ( EXCEPTION_EXECUTE_HANDLER )
789 {
790 self->Output(_T("Can't process symbol %hs\r\n"), pSymInfo->Name);
791 }
792
793 // continue with enumeration
794 return true;
795}
796
797HANDLE
798wxCrashReportImpl::OutputBasicContext(EXCEPTION_RECORD *pExceptionRecord,
799 CONTEXT *pCtx)
800{
801 // First print information about the type of fault
802 const DWORD dwCode = pExceptionRecord->ExceptionCode;
803 Output(_T("Exception code: %s (%#10x)\r\n"),
804 GetExceptionString(dwCode).c_str(), dwCode);
805
806 // Now print information about where the fault occured
807 TCHAR szFaultingModule[MAX_PATH];
808 DWORD section,
809 offset;
810 void * const pExceptionAddress = pExceptionRecord->ExceptionAddress;
811 if ( !GetLogicalAddress(pExceptionAddress,
812 szFaultingModule,
813 WXSIZEOF(szFaultingModule),
814 section, offset) )
815 {
816 section =
817 offset = 0;
818
819 wxStrcpy(szFaultingModule, _T("<< unknown >>"));
820 }
821
822 Output(_T("Fault address: %08x %02x:%08x %s\r\n"),
823 pExceptionAddress, section, offset, szFaultingModule);
824
825#ifdef _M_IX86
826 // Show the registers
827 Output( _T("\r\nRegisters:\r\n") );
828
829 Output(_T("EAX: %08x EBX: %08x ECX: %08x EDX: %08x ESI: %08x EDI: %08x\r\n"),
830 pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi);
831
832 Output(_T("CS:EIP: %04x:%08x SS:ESP: %04x:%08x EBP: %08x\r\n"),
833 pCtx->SegCs, pCtx->Eip, pCtx->SegSs, pCtx->Esp, pCtx->Ebp );
834 Output(_T("DS: %04x ES: %04x FS: %04x GS: %04x\r\n"),
835 pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
836 Output(_T("Flags: %08x\r\n"), pCtx->EFlags );
837#endif // _M_IX86
838
839 return ::GetModuleHandle(szFaultingModule);
840}
841
842void wxCrashReportImpl::OutputStack(const CONTEXT *pCtx, int flags)
843{
844 enum
845 {
846 Output_Stack,
847 Output_Locals,
848 Output_Max
849#ifndef _M_IX86
850 // can't show locals under other architectures
851 = Output_Locals
852#endif
853 };
854
855 for ( int step = 0; step < Output_Max; step++ )
856 {
857 // don't do things we're not asked for
858 if ( (step == Output_Stack) && !(flags & wxCRASH_REPORT_STACK) ||
859 (step == Output_Locals) && !(flags & wxCRASH_REPORT_LOCALS) )
860 {
861 continue;
862 }
863
864 // the context is going to be modified below so make a copy
865 CONTEXT ctx = *pCtx;
866
867 Output(_T("\r\n%s\r\n")
868 _T(" # Address Frame Function SourceFile\r\n"),
869 step == Output_Stack ? _T("Call stack") : _T("Local variables"));
870
871 DWORD dwMachineType = 0;
872
873 STACKFRAME sf;
874 wxZeroMemory(sf);
875
876#ifdef _M_IX86
877 // Initialize the STACKFRAME structure for the first call. This is
878 // only necessary for Intel CPUs, and isn't mentioned in the
879 // documentation.
880 sf.AddrPC.Offset = ctx.Eip;
881 sf.AddrPC.Mode = AddrModeFlat;
882 sf.AddrStack.Offset = ctx.Esp;
883 sf.AddrStack.Mode = AddrModeFlat;
884 sf.AddrFrame.Offset = ctx.Ebp;
885 sf.AddrFrame.Mode = AddrModeFlat;
886
887 dwMachineType = IMAGE_FILE_MACHINE_I386;
888#endif // _M_IX86
889
890 const HANDLE hProcess = GetCurrentProcess();
891 const HANDLE hThread = GetCurrentThread();
892
893 // first show just the call stack
894 int frame = 0;
895 for ( ;; )
896 {
897 // Get the next stack frame
898 if ( !StackWalk(dwMachineType,
899 hProcess,
900 hThread,
901 &sf,
902 &ctx,
903 0,
904 SymFunctionTableAccess,
905 SymGetModuleBase,
906 0) )
907 {
908 break;
909 }
910
911 // Basic sanity check to make sure the frame is OK.
912 if ( !sf.AddrFrame.Offset )
913 break;
914
915 Output(_T("%2d %08x %08x "),
916 frame++, sf.AddrPC.Offset, sf.AddrFrame.Offset);
917
918 // Get the name of the function for this stack frame entry
919 BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + 1024 ];
920 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
921 pSymbol->SizeOfStruct = sizeof(symbolBuffer);
922 pSymbol->MaxNameLen = 1024;
923
924 // Displacement of the input address, relative to the start of the
925 // symbol
926 DWORD64 symDisplacement = 0;
927
928 if ( SymFromAddr(hProcess, sf.AddrPC.Offset,
929 &symDisplacement,pSymbol) )
930 {
931 Output(_T("%hs()+%#") wxLongLongFmtSpec _T("x"),
932 pSymbol->Name, symDisplacement);
933 }
934 else // No symbol found. Print out the logical address instead.
935 {
936 TCHAR szModule[MAX_PATH];
937 DWORD section,
938 offset;
939
940 if ( !GetLogicalAddress((PVOID)sf.AddrPC.Offset,
941 szModule, sizeof(szModule),
942 section, offset) )
943 {
944 szModule[0] = _T('\0');
945 section =
946 offset = 0;
947 }
948
949 Output(_T("%04x:%08x %s"), section, offset, szModule);
950 }
951
952 // Get the source line for this stack frame entry
953 IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) };
954 DWORD dwLineDisplacement;
955 if ( SymGetLineFromAddr(hProcess, sf.AddrPC.Offset,
956 &dwLineDisplacement, &lineInfo ))
957 {
958 Output(_T(" %s line %u"),
959 lineInfo.FileName, lineInfo.LineNumber);
960 }
961
962 OutputEndl();
963
964
965#ifdef _M_IX86
966 // on the second iteration also show the local variables and
967 // parameters
968 if ( step == Output_Locals )
969 {
970 // Use SymSetContext to get just the locals/params for this
971 // frame
972 IMAGEHLP_STACK_FRAME imagehlpStackFrame;
973 imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
974 SymSetContext(hProcess, &imagehlpStackFrame, 0);
975
976 // Enumerate the locals/parameters
977 m_sfCurrent = &sf;
978 SymEnumSymbols(hProcess, 0, 0, EnumerateSymbolsCallback, this);
979
980 OutputEndl();
981 }
982#endif // _M_IX86
983 }
984 }
985}
986
987void wxCrashReportImpl::OutputGlobals(HANDLE hModule)
988{
989#ifdef _M_IX86
990 Output(_T("\r\nGlobal variables:\r\n"));
991
992 m_sfCurrent = NULL;
993 SymEnumSymbols(::GetCurrentProcess(), (DWORD64)hModule, NULL,
994 EnumerateSymbolsCallback, this);
995#endif // _M_IX86
996}
997
2bbc1b28
VZ
998#endif // wxUSE_MINIDUMP
999
1000bool wxCrashReportImpl::BindDbgHelpFunctions(const wxDynamicLibrary& dllDbgHelp)
50bea100
VZ
1001{
1002 #define LOAD_SYM_FUNCTION(name) \
87baeeaf 1003 name = (name ## _t) dllDbgHelp.GetSymbol(_T(#name)); \
50bea100
VZ
1004 if ( !name ) \
1005 { \
87baeeaf 1006 Output(_T("\r\nFunction ") _T(#name) \
50bea100
VZ
1007 _T("() not found.\r\n")); \
1008 return false; \
1009 }
1010
2bbc1b28
VZ
1011#if wxUSE_MINIDUMP
1012 LOAD_SYM_FUNCTION(MiniDumpWriteDump);
1013#else // !wxUSE_MINIDUMP
50bea100
VZ
1014 LOAD_SYM_FUNCTION(SymSetOptions);
1015 LOAD_SYM_FUNCTION(SymInitialize);
1016 LOAD_SYM_FUNCTION(StackWalk);
1017 LOAD_SYM_FUNCTION(SymFromAddr);
1018 LOAD_SYM_FUNCTION(SymFunctionTableAccess);
1019 LOAD_SYM_FUNCTION(SymGetModuleBase);
1020 LOAD_SYM_FUNCTION(SymGetLineFromAddr);
1021 LOAD_SYM_FUNCTION(SymSetContext);
1022 LOAD_SYM_FUNCTION(SymEnumSymbols);
1023 LOAD_SYM_FUNCTION(SymGetTypeInfo);
2bbc1b28 1024#endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP
50bea100
VZ
1025
1026 #undef LOAD_SYM_FUNCTION
1027
1028 return true;
1029}
1030
2bbc1b28
VZ
1031#if !wxUSE_MINIDUMP
1032
50bea100
VZ
1033/* static */
1034wxString wxCrashReportImpl::GetExceptionString(DWORD dwCode)
1035{
1036 wxString s;
1037
1038 #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break
1039
1040 switch ( dwCode )
1041 {
1042 CASE_EXCEPTION(ACCESS_VIOLATION);
1043 CASE_EXCEPTION(DATATYPE_MISALIGNMENT);
1044 CASE_EXCEPTION(BREAKPOINT);
1045 CASE_EXCEPTION(SINGLE_STEP);
1046 CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED);
1047 CASE_EXCEPTION(FLT_DENORMAL_OPERAND);
1048 CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO);
1049 CASE_EXCEPTION(FLT_INEXACT_RESULT);
1050 CASE_EXCEPTION(FLT_INVALID_OPERATION);
1051 CASE_EXCEPTION(FLT_OVERFLOW);
1052 CASE_EXCEPTION(FLT_STACK_CHECK);
1053 CASE_EXCEPTION(FLT_UNDERFLOW);
1054 CASE_EXCEPTION(INT_DIVIDE_BY_ZERO);
1055 CASE_EXCEPTION(INT_OVERFLOW);
1056 CASE_EXCEPTION(PRIV_INSTRUCTION);
1057 CASE_EXCEPTION(IN_PAGE_ERROR);
1058 CASE_EXCEPTION(ILLEGAL_INSTRUCTION);
1059 CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION);
1060 CASE_EXCEPTION(STACK_OVERFLOW);
1061 CASE_EXCEPTION(INVALID_DISPOSITION);
1062 CASE_EXCEPTION(GUARD_PAGE);
1063 CASE_EXCEPTION(INVALID_HANDLE);
1064
1065 default:
1066 // unknown exception, ask NTDLL for the name
1067 if ( !::FormatMessage
1068 (
1069 FORMAT_MESSAGE_IGNORE_INSERTS |
1070 FORMAT_MESSAGE_FROM_HMODULE,
1071 ::GetModuleHandle(_T("NTDLL.DLL")),
1072 dwCode,
1073 0,
1074 wxStringBuffer(s, 1024),
1075 1024,
1076 0
1077 ) )
1078 {
1079 s = _T("UNKNOWN_EXCEPTION");
1080 }
1081 }
1082
1083 #undef CASE_EXCEPTION
1084
1085 return s;
1086}
1087
2bbc1b28
VZ
1088#endif // !wxUSE_MINIDUMP
1089
0b7824d7
VZ
1090#endif // wxUSE_DBGHELP
1091
57ba8fb8
DS
1092bool wxCrashReportImpl::Generate(
1093#if wxUSE_DBGHELP
1094 int flags
1095#else
1096 int WXUNUSED(flags)
1097#endif
1098)
0b7824d7
VZ
1099{
1100 if ( m_hFile == INVALID_HANDLE_VALUE )
1101 return false;
1102
1103#if wxUSE_DBGHELP
1104 if ( !wxGlobalSEInformation )
1105 return false;
1106
2bbc1b28 1107#if !wxUSE_MINIDUMP
0b7824d7
VZ
1108 PEXCEPTION_RECORD pExceptionRecord = wxGlobalSEInformation->ExceptionRecord;
1109 PCONTEXT pCtx = wxGlobalSEInformation->ContextRecord;
1110
1111 if ( !pExceptionRecord || !pCtx )
1112 return false;
1113
1114 HANDLE hModuleCrash = OutputBasicContext(pExceptionRecord, pCtx);
2bbc1b28
VZ
1115#endif // !wxUSE_MINIDUMP
1116
8d008965
VZ
1117 // show to the user that we're doing something...
1118 BusyCursor busyCursor;
1119
1120 // user-specified crash report flags override those specified by the
1121 // programmer
1122 TCHAR envFlags[64];
1123 DWORD dwLen = ::GetEnvironmentVariable
1124 (
1125 _T("WX_CRASH_FLAGS"),
1126 envFlags,
1127 WXSIZEOF(envFlags)
1128 );
1129
1130 int flagsEnv;
1131 if ( dwLen && dwLen < WXSIZEOF(envFlags) &&
1132 wxSscanf(envFlags, _T("%d"), &flagsEnv) == 1 )
1133 {
1134 flags = flagsEnv;
1135 }
0b7824d7
VZ
1136
1137 // for everything else we need dbghelp.dll
1138 wxDynamicLibrary dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM);
1139 if ( dllDbgHelp.IsLoaded() )
1140 {
2bbc1b28 1141 if ( BindDbgHelpFunctions(dllDbgHelp) )
0b7824d7 1142 {
2bbc1b28
VZ
1143#if wxUSE_MINIDUMP
1144 MINIDUMP_EXCEPTION_INFORMATION minidumpExcInfo;
1145
1146 minidumpExcInfo.ThreadId = ::GetCurrentThreadId();
1147 minidumpExcInfo.ExceptionPointers = wxGlobalSEInformation;
1148 minidumpExcInfo.ClientPointers = FALSE; // in our own address space
1149
1150 // do generate the dump
8d008965
VZ
1151 MINIDUMP_TYPE dumpFlags;
1152 if ( flags & wxCRASH_REPORT_LOCALS )
1153 {
1154 // the only way to get local variables is to dump the entire
1155 // process memory space -- but this makes for huge (dozens or
1156 // even hundreds of Mb) files
1157 dumpFlags = MiniDumpWithFullMemory;
1158 }
1159 else if ( flags & wxCRASH_REPORT_GLOBALS )
1160 {
1161 // MiniDumpWriteDump() has the option for dumping just the data
1162 // segment which contains all globals -- exactly what we need
1163 dumpFlags = MiniDumpWithDataSegs;
1164 }
1165 else // minimal dump
1166 {
1167 dumpFlags = MiniDumpNormal;
1168 }
1169
2bbc1b28
VZ
1170 if ( !MiniDumpWriteDump
1171 (
1172 ::GetCurrentProcess(),
1173 ::GetCurrentProcessId(),
1174 m_hFile, // file to write to
8d008965 1175 dumpFlags, // kind of dump to craete
2bbc1b28
VZ
1176 &minidumpExcInfo,
1177 NULL, // no extra user-defined data
1178 NULL // no callbacks
1179 ) )
1180 {
1181 Output(_T("MiniDumpWriteDump() failed."));
1182
1183 return false;
1184 }
1185
1186 return true;
1187#else // !wxUSE_MINIDUMP
96f32e18 1188 SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME);
0b7824d7
VZ
1189
1190 // Initialize DbgHelp
1191 if ( SymInitialize(GetCurrentProcess(), NULL, TRUE /* invade */) )
1192 {
1193 OutputStack(pCtx, flags);
1194
1195 if ( hModuleCrash && (flags & wxCRASH_REPORT_GLOBALS) )
1196 {
1197 OutputGlobals(hModuleCrash);
1198 }
1199
1200 return true;
1201 }
2bbc1b28 1202#endif // !wxUSE_MINIDUMP
0b7824d7
VZ
1203 }
1204 else
1205 {
2bbc1b28
VZ
1206 Output(_T("\r\nPlease update your dbghelp.dll version, ")
1207 _T("at least version 5.1 is needed!\r\n")
1208 _T("(if you already have a new version, please ")
1209 _T("put it in the same directory where the program is.)\r\n"));
0b7824d7
VZ
1210 }
1211 }
2bbc1b28 1212 else // failed to load dbghelp.dll
0b7824d7
VZ
1213 {
1214 Output(_T("Please install dbghelp.dll available free of charge ")
1215 _T("from Microsoft to get more detailed crash information!"));
1216 }
1217
87baeeaf
VZ
1218 Output(_T("\r\nLatest dbghelp.dll is available at ")
1219 _T("http://www.microsoft.com/whdc/ddk/debugging/\r\n"));
0b7824d7
VZ
1220
1221#else // !wxUSE_DBGHELP
1222 Output(_T("Support for crash report generation was not included ")
1223 _T("in this wxWindows version."));
1224#endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
1225
2bbc1b28 1226 return false;
0b7824d7
VZ
1227}
1228
50bea100
VZ
1229// ----------------------------------------------------------------------------
1230// wxCrashReport
1231// ----------------------------------------------------------------------------
1232
1233/* static */
1234void wxCrashReport::SetFileName(const wxChar *filename)
1235{
1236 wxStrncpy(gs_reportFilename, filename, WXSIZEOF(gs_reportFilename) - 1);
1237 gs_reportFilename[WXSIZEOF(gs_reportFilename) - 1] = _T('\0');
1238}
1239
1240/* static */
1241const wxChar *wxCrashReport::GetFileName()
1242{
1243 return gs_reportFilename;
1244}
1245
1246/* static */
1247bool wxCrashReport::Generate(int flags)
1248{
1249 wxCrashReportImpl impl(gs_reportFilename);
1250
1251 return impl.Generate(flags);
1252}
1253
1254// ----------------------------------------------------------------------------
1255// wxApp::OnFatalException() support
1256// ----------------------------------------------------------------------------
1257
1258bool wxHandleFatalExceptions(bool doit)
1259{
1260 // assume this can only be called from the main thread
1261 gs_handleExceptions = doit;
1262
1263 if ( doit )
1264 {
1265 // try to find a place where we can put out report file later
1266 if ( !::GetTempPath
1267 (
1268 WXSIZEOF(gs_reportFilename),
1269 gs_reportFilename
1270 ) )
1271 {
1272 wxLogLastError(_T("GetTempPath"));
1273
1274 // when all else fails...
1275 wxStrcpy(gs_reportFilename, _T("c:\\"));
1276 }
1277
1278 // use PID and date to make the report file name more unique
1279 wxString fname = wxString::Format
1280 (
2bbc1b28
VZ
1281#if wxUSE_MINIDUMP
1282 _T("%s_%s_%lu.dmp"),
1283#else // !wxUSE_MINIDUMP
50bea100 1284 _T("%s_%s_%lu.rpt"),
2bbc1b28 1285#endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP
50bea100
VZ
1286 wxTheApp ? wxTheApp->GetAppName().c_str()
1287 : _T("wxwindows"),
1288 wxDateTime::Now().Format(_T("%Y%m%d")).c_str(),
1289 ::GetCurrentProcessId()
1290 );
1291
1292 wxStrncat(gs_reportFilename, fname,
d4cb34b0 1293 WXSIZEOF(gs_reportFilename) - wxStrlen(gs_reportFilename) - 1);
50bea100
VZ
1294 }
1295
1296 return true;
1297}
1298
1299extern unsigned long wxGlobalSEHandler(EXCEPTION_POINTERS *pExcPtrs)
1300{
1301 if ( gs_handleExceptions && wxTheApp )
1302 {
1303 // store the pointer to exception info
1304 wxGlobalSEInformation = pExcPtrs;
1305
1306 // give the user a chance to do something special about this
3f1d3756
VZ
1307 __try
1308 {
1309 wxTheApp->OnFatalException();
1310 }
1311 __except ( EXCEPTION_EXECUTE_HANDLER )
1312 {
1313 // nothing to do here, just ignore the exception inside the
1314 // exception handler
1315 ;
1316 }
50bea100
VZ
1317
1318 wxGlobalSEInformation = NULL;
1319
1320 // this will execute our handler and terminate the process
1321 return EXCEPTION_EXECUTE_HANDLER;
1322 }
1323
1324 return EXCEPTION_CONTINUE_SEARCH;
1325}
1326
1327#else // !wxUSE_ON_FATAL_EXCEPTION
1328
1329bool wxHandleFatalExceptions(bool WXUNUSED(doit))
1330{
1331 wxFAIL_MSG(_T("set wxUSE_ON_FATAL_EXCEPTION to 1 to use this function"));
1332
1333 return false;
1334}
1335
1336#endif // wxUSE_ON_FATAL_EXCEPTION/!wxUSE_ON_FATAL_EXCEPTION
1337