]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/crashrpt.cpp
Better fix
[wxWidgets.git] / src / msw / crashrpt.cpp
index 9ddcf4902935b92737092a9ee989174e98f26a94..d2a303abbe0dffa90cac70c6ce1042c5f2167dee 100644 (file)
@@ -1,6 +1,6 @@
 /////////////////////////////////////////////////////////////////////////////
 // Name:        msw/crashrpt.cpp
 /////////////////////////////////////////////////////////////////////////////
 // Name:        msw/crashrpt.cpp
-// Purpose:     helpers for structured exception handling (SEH)
+// Purpose:     code to generate crash dumps (minidumps)
 // Author:      Vadim Zeitlin
 // Modified by:
 // Created:     13.07.03
 // Author:      Vadim Zeitlin
 // Modified by:
 // Created:     13.07.03
@@ -9,11 +9,6 @@
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
-/*
-    The code in this file is heavily based on Matt Pietrek's column from
-    the March 2002 issue of MSDN Magazine.
- */
-
 // ============================================================================
 // declarations
 // ============================================================================
 // ============================================================================
 // declarations
 // ============================================================================
     #pragma hdrstop
 #endif
 
     #pragma hdrstop
 #endif
 
-#if wxUSE_ON_FATAL_EXCEPTION
+#if wxUSE_CRASHREPORT
 
 #ifndef WX_PRECOMP
 #endif  //WX_PRECOMP
 
 
 #ifndef WX_PRECOMP
 #endif  //WX_PRECOMP
 
-#include "wx/longlong.h"
-#include "wx/datetime.h"
-#include "wx/dynload.h"
-
+#include "wx/msw/debughlp.h"
 #include "wx/msw/crashrpt.h"
 
 #include "wx/msw/crashrpt.h"
 
-#include "wx/msw/wrapwin.h"
-#include <imagehlp.h>
-#include "wx/msw/private.h"
-
-// we need to determine whether we have the declarations for the function in
-// debughlp.dll version 5.81 (at least) and we check for DBHLPAPI to test this
-//
-// reasons:
-//  - VC6 version of imagehlp.h doesn't define it
-//  - VC7 one does
-//  - testing for compiler version doesn't work as you can install and use
-//    the new SDK headers with VC6
-//
-// in any case, the user may override by defining wxUSE_DBGHELP himself
-#ifndef wxUSE_DBGHELP
-    #ifdef DBHLPAPI  
-        #define wxUSE_DBGHELP 1
-    #else
-        #define wxUSE_DBGHELP 0
-    #endif
-#endif
-
-#if wxUSE_DBGHELP
-
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
-// types of imagehlp.h functions
-// ----------------------------------------------------------------------------
-
-typedef DWORD (WINAPI *SymSetOptions_t)(DWORD);
-typedef BOOL (WINAPI *SymInitialize_t)(HANDLE, LPSTR, BOOL);
-typedef BOOL (WINAPI *StackWalk_t)(DWORD, HANDLE, HANDLE, LPSTACKFRAME,
-                                   LPVOID, PREAD_PROCESS_MEMORY_ROUTINE,
-                                   PFUNCTION_TABLE_ACCESS_ROUTINE,
-                                   PGET_MODULE_BASE_ROUTINE,
-                                   PTRANSLATE_ADDRESS_ROUTINE);
-typedef BOOL (WINAPI *SymFromAddr_t)(HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO);
-typedef LPVOID (WINAPI *SymFunctionTableAccess_t)(HANDLE, DWORD);
-typedef DWORD (WINAPI *SymGetModuleBase_t)(HANDLE, DWORD);
-typedef BOOL (WINAPI *SymGetLineFromAddr_t)(HANDLE, DWORD,
-                                            PDWORD, PIMAGEHLP_LINE);
-typedef BOOL (WINAPI *SymSetContext_t)(HANDLE, PIMAGEHLP_STACK_FRAME,
-                                       PIMAGEHLP_CONTEXT);
-typedef BOOL (WINAPI *SymEnumSymbols_t)(HANDLE, ULONG64, PCSTR,
-                                        PSYM_ENUMERATESYMBOLS_CALLBACK, PVOID);
-typedef BOOL (WINAPI *SymGetTypeInfo_t)(HANDLE, DWORD64, ULONG,
-                                        IMAGEHLP_SYMBOL_TYPE_INFO, PVOID);
-
-// ----------------------------------------------------------------------------
-// constant
+// classes
 // ----------------------------------------------------------------------------
 
 // ----------------------------------------------------------------------------
 
-// Stolen from CVCONST.H in the DIA 2.0 SDK
-enum BasicType
-{
-    BASICTYPE_NOTYPE = 0,
-    BASICTYPE_VOID = 1,
-    BASICTYPE_CHAR = 2,
-    BASICTYPE_WCHAR = 3,
-    BASICTYPE_INT = 6,
-    BASICTYPE_UINT = 7,
-    BASICTYPE_FLOAT = 8,
-    BASICTYPE_BCD = 9,
-    BASICTYPE_BOOL = 10,
-    BASICTYPE_LONG = 13,
-    BASICTYPE_ULONG = 14,
-    BASICTYPE_CURRENCY = 25,
-    BASICTYPE_DATE = 26,
-    BASICTYPE_VARIANT = 27,
-    BASICTYPE_COMPLEX = 28,
-    BASICTYPE_BIT = 29,
-    BASICTYPE_BSTR = 30,
-    BASICTYPE_HRESULT = 31
-};
-
-// Same as above
-enum SymbolTag
+// low level wxBusyCursor replacement: we use Win32 API directly here instead
+// of going through wxWidgets calls as this could be dangerous
+class BusyCursor
 {
 {
-    SYMBOL_TAG_NULL,
-    SYMBOL_TAG_FUNCTION = 5,
-    SYMBOL_TAG_DATA = 7,
-    SYMBOL_TAG_PUBLIC = 10,             // appears in .DBGs
-    SYMBOL_TAG_UDT,
-    SYMBOL_TAG_ENUM,
-    SYMBOL_TAG_FUNCTION_TYPE,
-    SYMBOL_TAG_POINTER_TYPE,
-    SYMBOL_TAG_ARRAY_TYPE,
-    SYMBOL_TAG_BASE_TYPE,
-    SYMBOL_TAG_TYPEDEF,
-    SYMBOL_TAG_BASECLASS
-};
+public:
+    BusyCursor()
+    {
+        HCURSOR hcursorBusy = ::LoadCursor(NULL, IDC_WAIT);
+        m_hcursorOld = ::SetCursor(hcursorBusy);
+    }
 
 
-#endif // wxUSE_DBGHELP
+    ~BusyCursor()
+    {
+        if ( m_hcursorOld )
+        {
+            ::SetCursor(m_hcursorOld);
+        }
+    }
 
 
-// ----------------------------------------------------------------------------
-// classes
-// ----------------------------------------------------------------------------
+private:
+    HCURSOR m_hcursorOld;
+};
 
 // the real crash report generator
 class wxCrashReportImpl
 
 // the real crash report generator
 class wxCrashReportImpl
@@ -143,7 +65,7 @@ class wxCrashReportImpl
 public:
     wxCrashReportImpl(const wxChar *filename);
 
 public:
     wxCrashReportImpl(const wxChar *filename);
 
-    bool Generate(int flags);
+    bool Generate(int flags, EXCEPTION_POINTERS *ep);
 
     ~wxCrashReportImpl()
     {
 
     ~wxCrashReportImpl()
     {
@@ -154,97 +76,13 @@ public:
     }
 
 private:
     }
 
 private:
+
     // formatted output to m_hFile
     void Output(const wxChar *format, ...);
 
     // output end of line
     void OutputEndl() { Output(_T("\r\n")); }
 
     // formatted output to m_hFile
     void Output(const wxChar *format, ...);
 
     // output end of line
     void OutputEndl() { Output(_T("\r\n")); }
 
-#if wxUSE_DBGHELP
-    // translate exception code to its symbolic name
-    static wxString GetExceptionString(DWORD dwCode);
-
-    // return the type from "type index"
-    static BasicType GetBasicType(DWORD64 modBase, DWORD typeIndex);
-
-    // return the name for the type index
-    static wxString GetSymbolName(DWORD64 modBase, DWORD dwTypeIndex);
-
-    // return the string representation of the variable value
-    static wxString FormatSimpleValue(BasicType bt,
-                                      DWORD64 length,
-                                      PVOID pAddress);
-
-    // return string representation of a struct field (which may itself be a
-    // struct, of course)
-    static wxString FormatField(DWORD64 modBase,
-                                DWORD dwTypeIndex,
-                                void *pVariable,
-                                unsigned level);
-
-    // show the name and value of the given symbol
-    static wxString FormatSymbol(PSYMBOL_INFO pSym, STACKFRAME *sf);
-
-    // show value described by SYMBOL_INFO located at pVariable
-    static wxString FormatAnyValue(PSYMBOL_INFO pSym, void *pVariable);
-
-    // show value of possibly complex (user-defined) type
-    static wxString FormatUDT(DWORD64 modBase,
-                              DWORD dwTypeIndex,
-                              void *pVariable,
-                              unsigned level = 0);
-
-    // outputs information about the given symbol
-    void OutputSymbol(PSYMBOL_INFO pSymInfo, STACKFRAME *sf);
-
-    // load all the functions we need from dbghelp.dll, return true if all ok
-    bool ResolveSymFunctions(const wxDynamicLibrary& dllDbgHelp);
-
-    // map address to module (and also section:offset), retunr true if ok
-    static bool GetLogicalAddress(PVOID addr,
-                                  PTSTR szModule,
-                                  DWORD len,
-                                  DWORD& section,
-                                  DWORD& offset);
-
-    // callback used with SymEnumSymbols() to process all variables
-    static BOOL CALLBACK EnumerateSymbolsCallback(PSYMBOL_INFO  pSymInfo,
-                                                  ULONG         SymbolSize,
-                                                  PVOID         UserContext);
-
-
-    // show the general information about exception which should be always
-    // available
-    //
-    // returns the module of the handle where the crash occured
-    HANDLE OutputBasicContext(EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pCtx);
-
-    // output the call stack and local variables values
-    void OutputStack(const CONTEXT *pCtx, int flags);
-
-    // output the global variables values
-    void OutputGlobals(HANDLE hModuleCrash);
-
-
-    // the current stack frame (may be NULL)
-    STACKFRAME *m_sfCurrent;
-
-
-    // dynamically loaded dbghelp.dll functions
-    #define DECLARE_SYM_FUNCTION(func) static func ## _t func
-
-    DECLARE_SYM_FUNCTION(SymSetOptions);
-    DECLARE_SYM_FUNCTION(SymInitialize);
-    DECLARE_SYM_FUNCTION(StackWalk);
-    DECLARE_SYM_FUNCTION(SymFromAddr);
-    DECLARE_SYM_FUNCTION(SymFunctionTableAccess);
-    DECLARE_SYM_FUNCTION(SymGetModuleBase);
-    DECLARE_SYM_FUNCTION(SymGetLineFromAddr);
-    DECLARE_SYM_FUNCTION(SymSetContext);
-    DECLARE_SYM_FUNCTION(SymEnumSymbols);
-    DECLARE_SYM_FUNCTION(SymGetTypeInfo);
-#endif // wxUSE_DBGHELP
-
     // the handle of the report file
     HANDLE m_hFile;
 };
     // the handle of the report file
     HANDLE m_hFile;
 };
@@ -253,49 +91,25 @@ private:
 // globals
 // ----------------------------------------------------------------------------
 
 // globals
 // ----------------------------------------------------------------------------
 
-// global pointer to exception information, only valid inside OnFatalException
-extern WXDLLIMPEXP_BASE EXCEPTION_POINTERS *wxGlobalSEInformation = NULL;
-
-
-// flag telling us whether the application wants to handle exceptions at all
-static bool gs_handleExceptions = false;
-
 // the file name where the report about exception is written
 // the file name where the report about exception is written
+//
+// we use fixed buffer to avoid (big) dynamic allocations when the program
+// crashes
 static wxChar gs_reportFilename[MAX_PATH];
 
 static wxChar gs_reportFilename[MAX_PATH];
 
+// this is defined in msw/main.cpp
+extern EXCEPTION_POINTERS *wxGlobalSEInformation;
+
 // ============================================================================
 // implementation
 // ============================================================================
 
 // ============================================================================
 // implementation
 // ============================================================================
 
-#if wxUSE_DBGHELP
-
-#define DEFINE_SYM_FUNCTION(func) func ## _t wxCrashReportImpl::func = 0
-
-DEFINE_SYM_FUNCTION(SymSetOptions);
-DEFINE_SYM_FUNCTION(SymInitialize);
-DEFINE_SYM_FUNCTION(StackWalk);
-DEFINE_SYM_FUNCTION(SymFromAddr);
-DEFINE_SYM_FUNCTION(SymFunctionTableAccess);
-DEFINE_SYM_FUNCTION(SymGetModuleBase);
-DEFINE_SYM_FUNCTION(SymGetLineFromAddr);
-DEFINE_SYM_FUNCTION(SymSetContext);
-DEFINE_SYM_FUNCTION(SymEnumSymbols);
-DEFINE_SYM_FUNCTION(SymGetTypeInfo);
-
-#undef DEFINE_SYM_FUNCTION
-
-#endif // wxUSE_DBGHELP
-
 // ----------------------------------------------------------------------------
 // wxCrashReportImpl
 // ----------------------------------------------------------------------------
 
 wxCrashReportImpl::wxCrashReportImpl(const wxChar *filename)
 {
 // ----------------------------------------------------------------------------
 // wxCrashReportImpl
 // ----------------------------------------------------------------------------
 
 wxCrashReportImpl::wxCrashReportImpl(const wxChar *filename)
 {
-#if wxUSE_DBGHELP
-    m_sfCurrent = NULL;
-#endif // wxUSE_DBGHELP
-
     m_hFile = ::CreateFile
                 (
                     filename,
     m_hFile = ::CreateFile
                 (
                     filename,
@@ -316,638 +130,210 @@ void wxCrashReportImpl::Output(const wxChar *format, ...)
     DWORD cbWritten;
 
     wxString s = wxString::FormatV(format, argptr);
     DWORD cbWritten;
 
     wxString s = wxString::FormatV(format, argptr);
-    ::WriteFile(m_hFile, s, s.length() * sizeof(wxChar), &cbWritten, 0);
+
+    wxCharBuffer buf(s.mb_str(wxConvUTF8));
+    ::WriteFile(m_hFile, buf.data(), strlen(buf.data()), &cbWritten, 0);
 
     va_end(argptr);
 }
 
 
     va_end(argptr);
 }
 
-#if wxUSE_DBGHELP
-
-bool
-wxCrashReportImpl::GetLogicalAddress(PVOID addr,
-                                     PTSTR szModule,
-                                     DWORD len,
-                                     DWORD& section,
-                                     DWORD& offset)
+bool wxCrashReportImpl::Generate(int flags, EXCEPTION_POINTERS *ep)
 {
 {
-    MEMORY_BASIC_INFORMATION mbi;
-
-    if ( !::VirtualQuery(addr, &mbi, sizeof(mbi)) )
-        return false;
-
-    DWORD hMod = (DWORD)mbi.AllocationBase;
-
-    if ( !::GetModuleFileName((HMODULE)hMod, szModule, len) )
+    if ( m_hFile == INVALID_HANDLE_VALUE )
         return false;
 
         return false;
 
-    // Point to the DOS header in memory
-    PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
-
-    // From the DOS header, find the NT (PE) header
-    PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + pDosHdr->e_lfanew);
-
-    PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr );
-
-    DWORD rva = (DWORD)addr - hMod; // RVA is offset from module load address
+#if wxUSE_DBGHELP
+    if ( !ep )
+        ep = wxGlobalSEInformation;
 
 
-    // Iterate through the section table, looking for the one that encompasses
-    // the linear address.
-    const DWORD nSections = pNtHdr->FileHeader.NumberOfSections;
-    for ( DWORD i = 0; i < nSections; i++, pSection++ )
+    if ( !ep )
     {
     {
-        DWORD sectionStart = pSection->VirtualAddress;
-        DWORD sectionEnd = sectionStart
-                    + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);
-
-        // Is the address in this section?
-        if ( (rva >= sectionStart) && (rva <= sectionEnd) )
-        {
-            // Yes, address is in the section.  Calculate section and offset,
-            // and store in the "section" & "offset" params, which were
-            // passed by reference.
-            section = i + 1;
-            offset = rva - sectionStart;
-
-            return true;
-        }
+        Output(_T("Context for crash report generation not available."));
+        return false;
     }
 
     }
 
-    // failed to map to logical address...
-    return false;
-}
-
-/* static */ BasicType
-wxCrashReportImpl::GetBasicType(DWORD64 modBase, DWORD typeIndex)
-{
-    const HANDLE hProcess = GetCurrentProcess();
-
-    // try the index we have
-    BasicType bt;
-    if ( SymGetTypeInfo(hProcess, modBase, typeIndex, TI_GET_BASETYPE, &bt) )
-    {
-        return bt;
-    }
+    // show to the user that we're doing something...
+    BusyCursor busyCursor;
 
 
-    // if failed, try to get the "real" typeid first
-    DWORD typeId;
-    if ( SymGetTypeInfo(hProcess, modBase, typeIndex, TI_GET_TYPEID, &typeId)
-            &&
-         (typeId != typeIndex &&
-            SymGetTypeInfo(hProcess, modBase, typeId, TI_GET_BASETYPE, &bt)) )
+    // user-specified crash report flags override those specified by the
+    // programmer
+    TCHAR envFlags[64];
+    DWORD dwLen = ::GetEnvironmentVariable
+                    (
+                        _T("WX_CRASH_FLAGS"),
+                        envFlags,
+                        WXSIZEOF(envFlags)
+                    );
+
+    int flagsEnv;
+    if ( dwLen && dwLen < WXSIZEOF(envFlags) &&
+            wxSscanf(envFlags, _T("%d"), &flagsEnv) == 1 )
     {
     {
-        return bt;
+        flags = flagsEnv;
     }
 
     }
 
-    return BASICTYPE_NOTYPE;
-}
-
-/* static */ wxString
-wxCrashReportImpl::FormatSimpleValue(BasicType bt,
-                                     DWORD64 length,
-                                     PVOID pAddress)
-{
-    wxString s;
-    s.reserve(256);
-
-    // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
-    if ( length == 1 )
+    if ( wxDbgHelpDLL::Init() )
     {
     {
-        s.Printf(_T("%#04x"), *(PBYTE)pAddress);
-    }
-    else if ( length == 2 )
-    {
-        s.Printf(_T("%#06x"), *(PWORD)pAddress);
-    }
-    else if ( length == 4 )
-    {
-        bool handled = false;
+        MINIDUMP_EXCEPTION_INFORMATION minidumpExcInfo;
 
 
-        if ( bt == BASICTYPE_FLOAT )
-        {
-            s.Printf(_T("%f"), *(PFLOAT)pAddress);
+        minidumpExcInfo.ThreadId = ::GetCurrentThreadId();
+        minidumpExcInfo.ExceptionPointers = ep;
+        minidumpExcInfo.ClientPointers = FALSE; // in our own address space
 
 
-            handled = true;
-        }
-        else if ( bt == BASICTYPE_CHAR )
+        // do generate the dump
+        MINIDUMP_TYPE dumpFlags;
+        if ( flags & wxCRASH_REPORT_LOCALS )
         {
         {
-            static const size_t NUM_CHARS = 32;
-
-            const wxChar * const pc = *(PSTR *)pAddress;
-            if ( !::IsBadStringPtr(pc, NUM_CHARS) )
-            {
-                s << _T('"') << wxString(pc, NUM_CHARS) << _T('"');
-
-                handled = true;
-            }
+            // the only way to get local variables is to dump the entire
+            // process memory space -- but this makes for huge (dozens or
+            // even hundreds of Mb) files
+            dumpFlags = MiniDumpWithFullMemory;
         }
         }
-
-        if ( !handled )
+        else if ( flags & wxCRASH_REPORT_GLOBALS )
         {
         {
-            // treat just as an opaque DWORD
-            s.Printf(_T("%#x"), *(PDWORD)pAddress);
+            // MiniDumpWriteDump() has the option for dumping just the data
+            // segment which contains all globals -- exactly what we need
+            dumpFlags = MiniDumpWithDataSegs;
         }
         }
-    }
-    else if ( length == 8 )
-    {
-        if ( bt == BASICTYPE_FLOAT )
-        {
-            s.Printf(_T("%lf"), *(double *)pAddress);
-        }
-        else // opaque 64 bit value
+        else // minimal dump
         {
         {
-            s.Printf(_T("%#" wxLongLongFmtSpec _T("x")), *(PDWORD *)pAddress);
+            // the file size is not much bigger than when using MiniDumpNormal
+            // if we use the flags below, but the minidump is much more useful
+            // as it contains the values of many (but not all) local variables
+            dumpFlags = (MINIDUMP_TYPE)(MiniDumpScanMemory
+#if _MSC_VER > 1300
+                                        |MiniDumpWithIndirectlyReferencedMemory
+#endif
+                                        );
         }
         }
-    }
 
 
-    return s;
-}
-
-/* static */
-wxString wxCrashReportImpl::GetSymbolName(DWORD64 modBase, DWORD dwTypeIndex)
-{
-    wxString s;
+        if ( !wxDbgHelpDLL::MiniDumpWriteDump
+              (
+                ::GetCurrentProcess(),
+                ::GetCurrentProcessId(),
+                m_hFile,                    // file to write to
+                dumpFlags,                  // kind of dump to craete
+                &minidumpExcInfo,
+                NULL,                       // no extra user-defined data
+                NULL                        // no callbacks
+              ) )
+        {
+            Output(_T("MiniDumpWriteDump() failed."));
 
 
-    WCHAR *pwszTypeName;
-    if ( SymGetTypeInfo
-         (
-            GetCurrentProcess(),
-            modBase,
-            dwTypeIndex,
-            TI_GET_SYMNAME,
-            &pwszTypeName
-         ) )
-    {
-        s = wxConvCurrent->cWC2WX(pwszTypeName);
+            return false;
+        }
 
 
-        ::LocalFree(pwszTypeName);
+        return true;
     }
     }
-
-    return s;
-}
-
-// this is called for the struct members/base classes
-wxString
-wxCrashReportImpl::FormatField(DWORD64 modBase,
-                               DWORD dwTypeIndex,
-                               void *pVariable,
-                               unsigned level)
-{
-    wxString s;
-
-    const HANDLE hProcess = GetCurrentProcess();
-
-    DWORD dwTag = 0;
-    SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_GET_SYMTAG, &dwTag);
-
-    switch ( dwTag )
+    else // dbghelp.dll couldn't be loaded
     {
     {
-        case SYMBOL_TAG_UDT:
-        case SYMBOL_TAG_BASECLASS:
-            s = FormatUDT(modBase, dwTypeIndex, pVariable, level);
-            break;
-
-        case SYMBOL_TAG_FUNCTION:
-            // don't show
-            break;
-
-        default:
-            // try to treat all the rest as data even though it's not clear if
-            // it's really a good idea...
-
-            // Get the offset of the child member, relative to its parent
-            DWORD dwMemberOffset = 0;
-            SymGetTypeInfo(hProcess, modBase, dwTypeIndex,
-                           TI_GET_OFFSET, &dwMemberOffset);
-
-            // Get the real "TypeId" of the child.  We need this for the
-            // SymGetTypeInfo(TI_GET_LENGTH) call below.
-            DWORD typeId;
-            if ( !SymGetTypeInfo(hProcess, modBase, dwTypeIndex,
-                                 TI_GET_TYPEID, &typeId) )
-            {
-                typeId = dwTypeIndex;
-            }
-
-            // Get the size of the child member
-            ULONG64 size;
-            SymGetTypeInfo(hProcess, modBase, typeId, TI_GET_LENGTH, &size);
-
-            // Calculate the address of the member
-            DWORD_PTR dwFinalOffset = (DWORD_PTR)pVariable + dwMemberOffset;
-
-            BasicType basicType = GetBasicType(modBase, dwTypeIndex);
-
-            s = FormatSimpleValue(basicType, size, (PVOID)dwFinalOffset);
-            break;
-
+        Output(_T("%s"), wxDbgHelpDLL::GetErrorMessage().c_str());
     }
     }
+#else // !wxUSE_DBGHELP
+    wxUnusedVar(flags);
+    wxUnusedVar(ep);
 
 
-    if ( s.empty() )
-    {
-        // don't show if no value -- what for?
-        return s;
-    }
+    Output(_T("Support for crash report generation was not included ")
+           _T("in this wxWidgets version."));
+#endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
 
 
-    return wxString(_T('\t'), level + 1) +
-                GetSymbolName(modBase, dwTypeIndex) +
-                    _T(" = ") + s + _T("\r\n");
+    return false;
 }
 
 }
 
-// If it's a user defined type (UDT), recurse through its members until we're
-// at fundamental types.
-wxString
-wxCrashReportImpl::FormatUDT(DWORD64 modBase,
-                             DWORD dwTypeIndex,
-                             void *pVariable,
-                             unsigned level)
-{
-    wxString s;
-    s.reserve(512);
-    s = GetSymbolName(modBase, dwTypeIndex) + _T(" {\r\n");
-
-    const HANDLE hProcess = GetCurrentProcess();
-
-    // Determine how many children this type has.
-    DWORD dwChildrenCount = 0;
-    SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT,
-                   &dwChildrenCount);
-
-    // Prepare to get an array of "TypeIds", representing each of the children.
-    TI_FINDCHILDREN_PARAMS *children = (TI_FINDCHILDREN_PARAMS *)
-        malloc(sizeof(TI_FINDCHILDREN_PARAMS) +
-                    (dwChildrenCount - 1)*sizeof(ULONG));
-    if ( !children )
-        return s;
-
-    children->Count = dwChildrenCount;
-    children->Start = 0;
-
-    // Get the array of TypeIds, one for each child type
-    if ( !SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN,
-                         children) )
-    {
-        return s;
-    }
-
-    // Iterate through all children
-    for ( unsigned i = 0; i < dwChildrenCount; i++ )
-    {
-        s += FormatField(modBase, children->ChildId[i], pVariable, level + 1);
-    }
-
-    free(children);
-
-    s << wxString(_T('\t'), level + 1) << _T('}');
-
-    return s;
-}
+// ----------------------------------------------------------------------------
+// wxCrashReport
+// ----------------------------------------------------------------------------
 
 
-// return the string containing the symbol of the given symbol
-/* static */ wxString
-wxCrashReportImpl::FormatAnyValue(PSYMBOL_INFO pSym, void *pVariable)
+/* static */
+void wxCrashReport::SetFileName(const wxChar *filename)
 {
 {
-    DWORD dwTag = 0;
-    SymGetTypeInfo(GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
-                   TI_GET_SYMTAG, &dwTag);
-
-    wxString s;
-    switch ( dwTag )
-    {
-        case SYMBOL_TAG_FUNCTION:
-            break;
-
-        case SYMBOL_TAG_UDT:
-        case SYMBOL_TAG_BASECLASS:
-            // show UDT recursively
-            s = FormatUDT(pSym->ModBase, pSym->TypeIndex, pVariable);
-            break;
-
-        default:
-            // variable of simple type (but could be array which we don't
-            // handle correctly yet...), format it using its type and size
-            BasicType bt = GetBasicType(pSym->ModBase, pSym->TypeIndex);
-
-            s = FormatSimpleValue(bt, pSym->Size, pVariable);
-            break;
-
-    }
-
-    return s;
+    wxStrncpy(gs_reportFilename, filename, WXSIZEOF(gs_reportFilename) - 1);
+    gs_reportFilename[WXSIZEOF(gs_reportFilename) - 1] = _T('\0');
 }
 
 }
 
-// display contents and type of the given variable
-/* static */ wxString
-wxCrashReportImpl::FormatSymbol(PSYMBOL_INFO pSym, STACKFRAME *sf)
+/* static */
+const wxChar *wxCrashReport::GetFileName()
 {
 {
-    wxString s;
-
-    if ( pSym->Tag == SYMBOL_TAG_FUNCTION )
-    {
-        // If it's a function, don't do anything.
-        return s;
-    }
-
-    if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER )
-    {
-        // Don't try to report register variable
-        return s;
-    }
-
-    s.reserve(512);
-
-    // Indicate if the variable is a local or parameter
-    if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER )
-        s += _T("\t[param] ");
-    else if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL )
-        s += _T("\t[local] ");
-
-    // Will point to the variable's data in memory
-    DWORD_PTR pVariable = 0;
-
-    if ( (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE) && sf )
-    {
-        pVariable = sf->AddrFrame.Offset;
-        pVariable += (DWORD_PTR)pSym->Address;
-    }
-    else // It must be a global variable
-    {
-        pVariable = (DWORD_PTR)pSym->Address;
-    }
-
-    s << pSym->Name << _T(" = ") << FormatAnyValue(pSym, (PVOID)pVariable);
-
-    return s;
+    return gs_reportFilename;
 }
 
 }
 
-void
-wxCrashReportImpl::OutputSymbol(PSYMBOL_INFO pSymInfo, STACKFRAME  *sf)
+/* static */
+bool wxCrashReport::Generate(int flags, EXCEPTION_POINTERS *ep)
 {
 {
-    wxString s = FormatSymbol(pSymInfo, sf);
-    if ( !s.empty() )
-    {
-        Output(_T("%s\r\n"), s.c_str());
-    }
-    //else: not an interesting symbol
+    wxCrashReportImpl impl(gs_reportFilename);
+
+    return impl.Generate(flags, ep);
 }
 
 }
 
-// callback for SymEnumSymbols()
 /* static */
 /* static */
-BOOL CALLBACK
-wxCrashReportImpl::EnumerateSymbolsCallback(PSYMBOL_INFO  pSymInfo,
-                                            ULONG         WXUNUSED(SymbolSize),
-                                            PVOID         UserContext)
+bool wxCrashReport::GenerateNow(int flags)
 {
 {
-    wxCrashReportImpl *self = (wxCrashReportImpl *)UserContext;
+    bool rc = false;
 
     __try
     {
 
     __try
     {
-        self->OutputSymbol(pSymInfo, self->m_sfCurrent);
+        RaiseException(0x1976, 0, 0, NULL);
     }
     }
-    __except ( EXCEPTION_EXECUTE_HANDLER )
+    __except( rc = Generate(flags, (EXCEPTION_POINTERS *)GetExceptionInformation()),
+              EXCEPTION_CONTINUE_EXECUTION )
     {
     {
-        self->Output(_T("Can't process symbol %hs\r\n"), pSymInfo->Name);
+        // never executed because of EXCEPTION_CONTINUE_EXECUTION above
     }
 
     }
 
-    // continue with enumeration
-    return true;
+    return rc;
 }
 
 }
 
-HANDLE
-wxCrashReportImpl::OutputBasicContext(EXCEPTION_RECORD *pExceptionRecord,
-                                    CONTEXT *pCtx)
-{
-    // First print information about the type of fault
-    const DWORD dwCode = pExceptionRecord->ExceptionCode;
-    Output(_T("Exception code: %s (%#10x)\r\n"),
-           GetExceptionString(dwCode).c_str(), dwCode);
-
-    // Now print information about where the fault occured
-    TCHAR szFaultingModule[MAX_PATH];
-    DWORD section,
-          offset;
-    void * const pExceptionAddress = pExceptionRecord->ExceptionAddress;
-    if ( !GetLogicalAddress(pExceptionAddress,
-                            szFaultingModule,
-                            WXSIZEOF(szFaultingModule),
-                            section, offset) )
-    {
-        section =
-        offset = 0;
-
-        wxStrcpy(szFaultingModule, _T("<< unknown >>"));
-    }
-
-    Output(_T("Fault address:  %08x %02x:%08x %s\r\n"),
-           pExceptionAddress, section, offset, szFaultingModule);
-
-#ifdef _M_IX86
-    // Show the registers
-    Output( _T("\r\nRegisters:\r\n") );
-
-    Output(_T("EAX: %08x EBX: %08x ECX: %08x EDX: %08x ESI: %08x EDI: %08x\r\n"),
-            pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi);
-
-    Output(_T("CS:EIP: %04x:%08x SS:ESP: %04x:%08x  EBP: %08x\r\n"),
-           pCtx->SegCs, pCtx->Eip, pCtx->SegSs, pCtx->Esp, pCtx->Ebp );
-    Output(_T("DS: %04x  ES: %04x  FS: %04x  GS: %04x\r\n"),
-           pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
-    Output(_T("Flags: %08x\r\n"), pCtx->EFlags );
-#endif // _M_IX86
-
-    return ::GetModuleHandle(szFaultingModule);
-}
+// ----------------------------------------------------------------------------
+// wxCrashContext
+// ----------------------------------------------------------------------------
 
 
-void wxCrashReportImpl::OutputStack(const CONTEXT *pCtx, int flags)
+wxCrashContext::wxCrashContext(_EXCEPTION_POINTERS *ep)
 {
 {
-    enum
-    {
-        Output_Stack,
-        Output_Locals,
-        Output_Max
-#ifndef _M_IX86
-        // can't show locals under other architectures
-            = Output_Locals
-#endif
-    };
+    wxZeroMemory(*this);
 
 
-    for ( int step = 0; step < Output_Max; step++ )
+    if ( !ep )
     {
     {
-        // don't do things we're not asked for
-        if ( (step == Output_Stack) && !(flags & wxCRASH_REPORT_STACK) ||
-                (step == Output_Locals) && !(flags & wxCRASH_REPORT_LOCALS) )
-        {
-            continue;
-        }
-
-        // the context is going to be modified below so make a copy
-        CONTEXT ctx = *pCtx;
-
-        Output(_T("\r\n%s\r\n")
-               _T(" # Address   Frame     Function            SourceFile\r\n"),
-               step == Output_Stack ? _T("Call stack") : _T("Local variables"));
-
-        DWORD dwMachineType = 0;
-
-        STACKFRAME sf;
-        wxZeroMemory(sf);
-
-#ifdef _M_IX86
-        // Initialize the STACKFRAME structure for the first call.  This is
-        // only necessary for Intel CPUs, and isn't mentioned in the
-        // documentation.
-        sf.AddrPC.Offset       = ctx.Eip;
-        sf.AddrPC.Mode         = AddrModeFlat;
-        sf.AddrStack.Offset    = ctx.Esp;
-        sf.AddrStack.Mode      = AddrModeFlat;
-        sf.AddrFrame.Offset    = ctx.Ebp;
-        sf.AddrFrame.Mode      = AddrModeFlat;
-
-        dwMachineType = IMAGE_FILE_MACHINE_I386;
-#endif // _M_IX86
-
-        const HANDLE hProcess = GetCurrentProcess();
-        const HANDLE hThread = GetCurrentThread();
-
-        // first show just the call stack
-        int frame = 0;
-        for ( ;; )
-        {
-            // Get the next stack frame
-            if ( !StackWalk(dwMachineType,
-                            hProcess,
-                            hThread,
-                            &sf,
-                            &ctx,
-                            0,
-                            SymFunctionTableAccess,
-                            SymGetModuleBase,
-                            0) )
-            {
-                break;
-            }
-
-            // Basic sanity check to make sure the frame is OK.
-            if ( !sf.AddrFrame.Offset )
-                break;
-
-            Output(_T("%2d %08x  %08x  "),
-                   frame++, sf.AddrPC.Offset, sf.AddrFrame.Offset);
-
-            // Get the name of the function for this stack frame entry
-            BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + 1024 ];
-            PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
-            pSymbol->SizeOfStruct = sizeof(symbolBuffer);
-            pSymbol->MaxNameLen = 1024;
-
-            // Displacement of the input address, relative to the start of the
-            // symbol
-            DWORD64 symDisplacement = 0;
-
-            if ( SymFromAddr(hProcess, sf.AddrPC.Offset,
-                             &symDisplacement,pSymbol) )
-            {
-                Output(_T("%hs()+%#") wxLongLongFmtSpec _T("x"),
-                       pSymbol->Name, symDisplacement);
-            }
-            else    // No symbol found.  Print out the logical address instead.
-            {
-                TCHAR szModule[MAX_PATH];
-                DWORD section,
-                      offset;
-
-                if ( !GetLogicalAddress((PVOID)sf.AddrPC.Offset,
-                                        szModule, sizeof(szModule),
-                                        section, offset) )
-                {
-                    szModule[0] = _T('\0');
-                    section =
-                    offset = 0;
-                }
-
-                Output(_T("%04x:%08x %s"), section, offset, szModule);
-            }
-
-            // Get the source line for this stack frame entry
-            IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) };
-            DWORD dwLineDisplacement;
-            if ( SymGetLineFromAddr(hProcess, sf.AddrPC.Offset,
-                                    &dwLineDisplacement, &lineInfo ))
-            {
-                Output(_T("  %s line %u"),
-                       lineInfo.FileName, lineInfo.LineNumber);
-            }
-
-            OutputEndl();
-
-
-#ifdef _M_IX86
-            // on the second iteration also show the local variables and
-            // parameters
-            if ( step == Output_Locals )
-            {
-                // Use SymSetContext to get just the locals/params for this
-                // frame
-                IMAGEHLP_STACK_FRAME imagehlpStackFrame;
-                imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
-                SymSetContext(hProcess, &imagehlpStackFrame, 0);
-
-                // Enumerate the locals/parameters
-                m_sfCurrent = &sf;
-                SymEnumSymbols(hProcess, 0, 0, EnumerateSymbolsCallback, this);
-
-                OutputEndl();
-            }
-#endif // _M_IX86
-        }
+        wxCHECK_RET( wxGlobalSEInformation, _T("no exception info available") );
+        ep = wxGlobalSEInformation;
     }
     }
-}
-
-void wxCrashReportImpl::OutputGlobals(HANDLE hModule)
-{
-#ifdef _M_IX86
-    Output(_T("\r\nGlobal variables:\r\n"));
-
-    m_sfCurrent = NULL;
-    SymEnumSymbols(::GetCurrentProcess(), (DWORD64)hModule, NULL,
-                   EnumerateSymbolsCallback, this);
-#endif // _M_IX86
-}
-
-bool wxCrashReportImpl::ResolveSymFunctions(const wxDynamicLibrary& dllDbgHelp)
-{
-    #define LOAD_SYM_FUNCTION(name)                                           \
-        name = (name ## _t) dllDbgHelp.GetSymbol(#name);                      \
-        if ( !name )                                                          \
-        {                                                                     \
-            Output(_T("\r\nFunction ") __XFILE__(#name)                       \
-                   _T("() not found.\r\n"));                                  \
-            return false;                                                     \
-        }
-
-    LOAD_SYM_FUNCTION(SymSetOptions);
-    LOAD_SYM_FUNCTION(SymInitialize);
-    LOAD_SYM_FUNCTION(StackWalk);
-    LOAD_SYM_FUNCTION(SymFromAddr);
-    LOAD_SYM_FUNCTION(SymFunctionTableAccess);
-    LOAD_SYM_FUNCTION(SymGetModuleBase);
-    LOAD_SYM_FUNCTION(SymGetLineFromAddr);
-    LOAD_SYM_FUNCTION(SymSetContext);
-    LOAD_SYM_FUNCTION(SymEnumSymbols);
-    LOAD_SYM_FUNCTION(SymGetTypeInfo);
-
-    #undef LOAD_SYM_FUNCTION
 
 
-    return true;
+    // TODO: we could also get the operation (read/write) and address for which
+    //       it failed for EXCEPTION_ACCESS_VIOLATION code
+    const EXCEPTION_RECORD& rec = *ep->ExceptionRecord;
+    code = rec.ExceptionCode;
+    addr = rec.ExceptionAddress;
+
+#ifdef __INTEL__
+    const CONTEXT& ctx = *ep->ContextRecord;
+    regs.eax = ctx.Eax;
+    regs.ebx = ctx.Ebx;
+    regs.ecx = ctx.Ecx;
+    regs.edx = ctx.Edx;
+    regs.esi = ctx.Esi;
+    regs.edi = ctx.Edi;
+
+    regs.ebp = ctx.Ebp;
+    regs.esp = ctx.Esp;
+    regs.eip = ctx.Eip;
+
+    regs.cs = ctx.SegCs;
+    regs.ds = ctx.SegDs;
+    regs.es = ctx.SegEs;
+    regs.fs = ctx.SegFs;
+    regs.gs = ctx.SegGs;
+    regs.ss = ctx.SegSs;
+
+    regs.flags = ctx.EFlags;
+#endif // __INTEL__
 }
 
 }
 
-/* static */
-wxString wxCrashReportImpl::GetExceptionString(DWORD dwCode)
+wxString wxCrashContext::GetExceptionString() const
 {
     wxString s;
 
     #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break
 
 {
     wxString s;
 
     #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break
 
-    switch ( dwCode )
+    switch ( code )
     {
         CASE_EXCEPTION(ACCESS_VIOLATION);
         CASE_EXCEPTION(DATATYPE_MISALIGNMENT);
     {
         CASE_EXCEPTION(ACCESS_VIOLATION);
         CASE_EXCEPTION(DATATYPE_MISALIGNMENT);
@@ -979,14 +365,14 @@ wxString wxCrashReportImpl::GetExceptionString(DWORD dwCode)
                      FORMAT_MESSAGE_IGNORE_INSERTS |
                      FORMAT_MESSAGE_FROM_HMODULE,
                      ::GetModuleHandle(_T("NTDLL.DLL")),
                      FORMAT_MESSAGE_IGNORE_INSERTS |
                      FORMAT_MESSAGE_FROM_HMODULE,
                      ::GetModuleHandle(_T("NTDLL.DLL")),
-                     dwCode,
+                     code,
                      0,
                      wxStringBuffer(s, 1024),
                      1024,
                      0
                     ) )
             {
                      0,
                      wxStringBuffer(s, 1024),
                      1024,
                      0
                     ) )
             {
-                s = _T("UNKNOWN_EXCEPTION");
+                s.Printf(_T("UNKNOWN_EXCEPTION(%d)"), code);
             }
     }
 
             }
     }
 
@@ -995,178 +381,5 @@ wxString wxCrashReportImpl::GetExceptionString(DWORD dwCode)
     return s;
 }
 
     return s;
 }
 
-#endif // wxUSE_DBGHELP
-
-// Remove warning
-#if wxUSE_DBGHELP
-#define _WXUNUSED(x) x
-#else
-#define _WXUNUSED WXUNUSED
-#endif
-
-bool wxCrashReportImpl::Generate(int _WXUNUSED(flags))
-{
-    if ( m_hFile == INVALID_HANDLE_VALUE )
-        return false;
-
-#if wxUSE_DBGHELP
-    if ( !wxGlobalSEInformation )
-        return false;
-
-    PEXCEPTION_RECORD pExceptionRecord = wxGlobalSEInformation->ExceptionRecord;
-    PCONTEXT pCtx = wxGlobalSEInformation->ContextRecord;
-
-    if ( !pExceptionRecord || !pCtx )
-        return false;
-
-    HANDLE hModuleCrash = OutputBasicContext(pExceptionRecord, pCtx);
-
-    // for everything else we need dbghelp.dll
-    wxDynamicLibrary dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM);
-    if ( dllDbgHelp.IsLoaded() )
-    {
-        if ( ResolveSymFunctions(dllDbgHelp) )
-        {
-            SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME);
-
-            // Initialize DbgHelp
-            if ( SymInitialize(GetCurrentProcess(), NULL, TRUE /* invade */) )
-            {
-                OutputStack(pCtx, flags);
-
-                if ( hModuleCrash && (flags & wxCRASH_REPORT_GLOBALS) )
-                {
-                    OutputGlobals(hModuleCrash);
-                }
-
-                return true;
-            }
-        }
-        else
-        {
-            Output(_T("Please update your dbghelp.dll version, "
-                      "at least version 5.1 is needed!\r\n"));
-        }
-    }
-    else
-    {
-        Output(_T("Please install dbghelp.dll available free of charge ")
-               _T("from Microsoft to get more detailed crash information!"));
-    }
-
-    Output(_T("\r\nLatest dbghelp.dll is available at "
-              "http://www.microsoft.com/whdc/ddk/debugging/\r\n"));
-
-#else // !wxUSE_DBGHELP
-    Output(_T("Support for crash report generation was not included ")
-           _T("in this wxWindows version."));
-#endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
-
-    return true;
-}
-
-// ----------------------------------------------------------------------------
-// wxCrashReport
-// ----------------------------------------------------------------------------
-
-/* static */
-void wxCrashReport::SetFileName(const wxChar *filename)
-{
-    wxStrncpy(gs_reportFilename, filename, WXSIZEOF(gs_reportFilename) - 1);
-    gs_reportFilename[WXSIZEOF(gs_reportFilename) - 1] = _T('\0');
-}
-
-/* static */
-const wxChar *wxCrashReport::GetFileName()
-{
-    return gs_reportFilename;
-}
-
-/* static */
-bool wxCrashReport::Generate(int flags)
-{
-    wxCrashReportImpl impl(gs_reportFilename);
-
-    return impl.Generate(flags);
-}
-
-// ----------------------------------------------------------------------------
-// wxApp::OnFatalException() support
-// ----------------------------------------------------------------------------
-
-bool wxHandleFatalExceptions(bool doit)
-{
-    // assume this can only be called from the main thread
-    gs_handleExceptions = doit;
-
-    if ( doit )
-    {
-        // try to find a place where we can put out report file later
-        if ( !::GetTempPath
-                (
-                    WXSIZEOF(gs_reportFilename),
-                    gs_reportFilename
-                ) )
-        {
-            wxLogLastError(_T("GetTempPath"));
-
-            // when all else fails...
-            wxStrcpy(gs_reportFilename, _T("c:\\"));
-        }
-
-        // use PID and date to make the report file name more unique
-        wxString fname = wxString::Format
-                         (
-                            _T("%s_%s_%lu.rpt"),
-                            wxTheApp ? wxTheApp->GetAppName().c_str()
-                                     : _T("wxwindows"),
-                            wxDateTime::Now().Format(_T("%Y%m%d")).c_str(),
-                            ::GetCurrentProcessId()
-                         );
-
-        wxStrncat(gs_reportFilename, fname,
-                  WXSIZEOF(gs_reportFilename) - wxStrlen(gs_reportFilename) - 1);
-    }
-
-    return true;
-}
-
-extern unsigned long wxGlobalSEHandler(EXCEPTION_POINTERS *pExcPtrs)
-{
-    if ( gs_handleExceptions && wxTheApp )
-    {
-        // store the pointer to exception info
-        wxGlobalSEInformation = pExcPtrs;
-
-        // give the user a chance to do something special about this
-        __try
-        {
-            wxTheApp->OnFatalException();
-        }
-        __except ( EXCEPTION_EXECUTE_HANDLER )
-        {
-            // nothing to do here, just ignore the exception inside the
-            // exception handler
-            ;
-        }
-
-        wxGlobalSEInformation = NULL;
-
-        // this will execute our handler and terminate the process
-        return EXCEPTION_EXECUTE_HANDLER;
-    }
-
-    return EXCEPTION_CONTINUE_SEARCH;
-}
-
-#else // !wxUSE_ON_FATAL_EXCEPTION
-
-bool wxHandleFatalExceptions(bool WXUNUSED(doit))
-{
-    wxFAIL_MSG(_T("set wxUSE_ON_FATAL_EXCEPTION to 1 to use this function"));
-
-    return false;
-}
-
-#endif // wxUSE_ON_FATAL_EXCEPTION/!wxUSE_ON_FATAL_EXCEPTION
+#endif // wxUSE_CRASHREPORT/!wxUSE_CRASHREPORT