wxWidgets/samples/except/except.cpp
Vadim Zeitlin 55fd62c1e3 Open debugger at the location of failing assert, if possible.
Break into the debugger in the function containing the assert that failed
instead of inside wxWidgets assert handler which is several (~8) levels below
the last line of the user code. This is much more useful in practice and also
less confusing.

Currently this only works for MSVC as the other compilers don't have any
__debugbreak intrinsice equivalent.

Also update the except sample to test wxTrap() directly too.

Closes #11184.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@73124 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2012-12-04 00:39:29 +00:00

550 lines
16 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: samples/except/except.cpp
// Purpose: shows how C++ exceptions can be used in wxWidgets
// Author: Vadim Zeitlin
// Modified by:
// Created: 2003-09-17
// RCS-ID: $Id$
// Copyright: (c) 2003-2005 Vadim Zeitlin
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if !wxUSE_EXCEPTIONS
#error "This sample only works with wxUSE_EXCEPTIONS == 1"
#endif // !wxUSE_EXCEPTIONS
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/log.h"
#include "wx/app.h"
#include "wx/frame.h"
#include "wx/dialog.h"
#include "wx/menu.h"
#include "wx/button.h"
#include "wx/sizer.h"
#include "wx/utils.h"
#include "wx/msgdlg.h"
#include "wx/icon.h"
#include "wx/thread.h"
#endif
// ----------------------------------------------------------------------------
// resources
// ----------------------------------------------------------------------------
// the application icon (under Windows and OS/2 it is in resources)
#ifndef wxHAS_IMAGES_IN_RESOURCES
#include "../sample.xpm"
#endif
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
static void DoCrash()
{
char *p = 0;
strcpy(p, "Let's crash");
}
// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------
// Define a new application type, each program should derive a class from wxApp
class MyApp : public wxApp
{
public:
// override base class virtuals
// ----------------------------
// program startup
virtual bool OnInit();
// 2nd-level exception handling: we get all the exceptions occurring in any
// event handler here
virtual bool OnExceptionInMainLoop();
// 3rd, and final, level exception handling: whenever an unhandled
// exception is caught, this function is called
virtual void OnUnhandledException();
// and now for something different: this function is called in case of a
// crash (e.g. dereferencing null pointer, division by 0, ...)
virtual void OnFatalException();
// you can override this function to do something different (e.g. log the
// assert to file) whenever an assertion fails
virtual void OnAssertFailure(const wxChar *file,
int line,
const wxChar *func,
const wxChar *cond,
const wxChar *msg);
};
// Define a new frame type: this is going to be our main frame
class MyFrame : public wxFrame
{
public:
// ctor(s)
MyFrame();
// event handlers (these functions should _not_ be virtual)
void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
void OnDialog(wxCommandEvent& event);
void OnThrowInt(wxCommandEvent& event);
void OnThrowString(wxCommandEvent& event);
void OnThrowObject(wxCommandEvent& event);
void OnThrowUnhandled(wxCommandEvent& event);
void OnCrash(wxCommandEvent& event);
void OnTrap(wxCommandEvent& event);
#if wxUSE_ON_FATAL_EXCEPTION
void OnHandleCrash(wxCommandEvent& event);
#endif
protected:
// 1st-level exception handling: we overload ProcessEvent() to be able to
// catch exceptions which occur in MyFrame methods here
virtual bool ProcessEvent(wxEvent& event);
// provoke assert in main or worker thread
//
// this is used to show how an assert failure message box looks like
void OnShowAssert(wxCommandEvent& event);
#if wxUSE_THREADS
void OnShowAssertInThread(wxCommandEvent& event);
#endif // wxUSE_THREADS
private:
// any class wishing to process wxWidgets events must use this macro
DECLARE_EVENT_TABLE()
};
// A simple dialog which has only some buttons to throw exceptions
class MyDialog : public wxDialog
{
public:
MyDialog(wxFrame *parent);
// event handlers
void OnThrowInt(wxCommandEvent& event);
void OnThrowObject(wxCommandEvent& event);
void OnCrash(wxCommandEvent& event);
private:
DECLARE_EVENT_TABLE()
};
// A trivial exception class
class MyException
{
public:
MyException(const wxString& msg) : m_msg(msg) { }
const wxChar *what() const { return m_msg.c_str(); }
private:
wxString m_msg;
};
// Another exception class which just has to be different from anything else
class UnhandledException
{
};
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
// IDs for the controls and the menu commands
enum
{
// control ids and menu items
Except_ThrowInt = wxID_HIGHEST,
Except_ThrowString,
Except_ThrowObject,
Except_ThrowUnhandled,
Except_Crash,
Except_Trap,
#if wxUSE_ON_FATAL_EXCEPTION
Except_HandleCrash,
#endif // wxUSE_ON_FATAL_EXCEPTION
Except_ShowAssert,
#if wxUSE_THREADS
Except_ShowAssertInThread,
#endif // wxUSE_THREADS
Except_Dialog,
Except_Quit = wxID_EXIT,
Except_About = wxID_ABOUT
};
// ----------------------------------------------------------------------------
// event tables and other macros for wxWidgets
// ----------------------------------------------------------------------------
// the event tables connect the wxWidgets events with the functions (event
// handlers) which process them. It can be also done at run-time, but for the
// simple menu events like this the static method is much simpler.
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(Except_Quit, MyFrame::OnQuit)
EVT_MENU(Except_About, MyFrame::OnAbout)
EVT_MENU(Except_Dialog, MyFrame::OnDialog)
EVT_MENU(Except_ThrowInt, MyFrame::OnThrowInt)
EVT_MENU(Except_ThrowString, MyFrame::OnThrowString)
EVT_MENU(Except_ThrowObject, MyFrame::OnThrowObject)
EVT_MENU(Except_ThrowUnhandled, MyFrame::OnThrowUnhandled)
EVT_MENU(Except_Crash, MyFrame::OnCrash)
EVT_MENU(Except_Trap, MyFrame::OnTrap)
#if wxUSE_ON_FATAL_EXCEPTION
EVT_MENU(Except_HandleCrash, MyFrame::OnHandleCrash)
#endif // wxUSE_ON_FATAL_EXCEPTION
EVT_MENU(Except_ShowAssert, MyFrame::OnShowAssert)
#if wxUSE_THREADS
EVT_MENU(Except_ShowAssertInThread, MyFrame::OnShowAssertInThread)
#endif // wxUSE_THREADS
END_EVENT_TABLE()
BEGIN_EVENT_TABLE(MyDialog, wxDialog)
EVT_BUTTON(Except_ThrowInt, MyDialog::OnThrowInt)
EVT_BUTTON(Except_ThrowObject, MyDialog::OnThrowObject)
EVT_BUTTON(Except_Crash, MyDialog::OnCrash)
END_EVENT_TABLE()
// Create a new application object: this macro will allow wxWidgets to create
// the application object during program execution (it's better than using a
// static object for many reasons) and also implements the accessor function
// wxGetApp() which will return the reference of the right type (i.e. MyApp and
// not wxApp)
IMPLEMENT_APP(MyApp)
// ============================================================================
// MyApp implementation
// ============================================================================
// 'Main program' equivalent: the program execution "starts" here
bool MyApp::OnInit()
{
if ( !wxApp::OnInit() )
return false;
// create the main application window
MyFrame *frame = new MyFrame();
// and show it (the frames, unlike simple controls, are not shown when
// created initially)
frame->Show(true);
// success: wxApp::OnRun() will be called which will enter the main message
// loop and the application will run. If we returned false here, the
// application would exit immediately.
return true;
}
bool MyApp::OnExceptionInMainLoop()
{
try
{
throw;
}
catch ( int i )
{
wxLogWarning(wxT("Caught an int %d in MyApp."), i);
}
catch ( MyException& e )
{
wxLogWarning(wxT("Caught MyException(%s) in MyApp."), e.what());
}
catch ( ... )
{
throw;
}
return true;
}
void MyApp::OnUnhandledException()
{
// this shows how we may let some exception propagate uncaught
try
{
throw;
}
catch ( UnhandledException& )
{
throw;
}
catch ( ... )
{
wxMessageBox(wxT("Unhandled exception caught, program will terminate."),
wxT("wxExcept Sample"), wxOK | wxICON_ERROR);
}
}
void MyApp::OnFatalException()
{
wxMessageBox(wxT("Program has crashed and will terminate."),
wxT("wxExcept Sample"), wxOK | wxICON_ERROR);
}
void MyApp::OnAssertFailure(const wxChar *file,
int line,
const wxChar *func,
const wxChar *cond,
const wxChar *msg)
{
// take care to not show the message box from a worker thread, this doesn't
// work as it doesn't have any event loop
if ( !wxIsMainThread() ||
wxMessageBox
(
wxString::Format("An assert failed in %s().", func) +
"\n"
"Do you want to call the default assert handler?",
"wxExcept Sample",
wxYES_NO | wxICON_QUESTION
) == wxYES )
{
wxApp::OnAssertFailure(file, line, func, cond, msg);
}
}
// ============================================================================
// MyFrame implementation
// ============================================================================
// frame constructor
MyFrame::MyFrame()
: wxFrame(NULL, wxID_ANY, wxT("Except wxWidgets App"),
wxPoint(50, 50), wxSize(450, 340))
{
// set the frame icon
SetIcon(wxICON(sample));
#if wxUSE_MENUS
// create a menu bar
wxMenu *menuFile = new wxMenu;
menuFile->Append(Except_Dialog, wxT("Show &dialog\tCtrl-D"));
menuFile->AppendSeparator();
menuFile->Append(Except_ThrowInt, wxT("Throw an &int\tCtrl-I"));
menuFile->Append(Except_ThrowString, wxT("Throw a &string\tCtrl-S"));
menuFile->Append(Except_ThrowObject, wxT("Throw an &object\tCtrl-O"));
menuFile->Append(Except_ThrowUnhandled,
wxT("Throw &unhandled exception\tCtrl-U"));
menuFile->Append(Except_Crash, wxT("&Crash\tCtrl-C"));
menuFile->Append(Except_Trap, "&Trap\tCtrl-T",
"Break into the debugger (if one is running)");
menuFile->AppendSeparator();
#if wxUSE_ON_FATAL_EXCEPTION
menuFile->AppendCheckItem(Except_HandleCrash, wxT("&Handle crashes\tCtrl-H"));
menuFile->AppendSeparator();
#endif // wxUSE_ON_FATAL_EXCEPTION
menuFile->Append(Except_ShowAssert, wxT("Provoke &assert failure\tCtrl-A"));
#if wxUSE_THREADS
menuFile->Append(Except_ShowAssertInThread,
wxT("Assert failure in &thread\tShift-Ctrl-A"));
#endif // wxUSE_THREADS
menuFile->AppendSeparator();
menuFile->Append(Except_Quit, wxT("E&xit\tCtrl-Q"), wxT("Quit this program"));
wxMenu *helpMenu = new wxMenu;
helpMenu->Append(Except_About, wxT("&About\tF1"), wxT("Show about dialog"));
// now append the freshly created menu to the menu bar...
wxMenuBar *menuBar = new wxMenuBar();
menuBar->Append(menuFile, wxT("&File"));
menuBar->Append(helpMenu, wxT("&Help"));
// ... and attach this menu bar to the frame
SetMenuBar(menuBar);
#endif // wxUSE_MENUS
#if wxUSE_STATUSBAR && !defined(__WXWINCE__)
// create a status bar just for fun (by default with 1 pane only)
CreateStatusBar(2);
SetStatusText(wxT("Welcome to wxWidgets!"));
#endif // wxUSE_STATUSBAR
}
bool MyFrame::ProcessEvent(wxEvent& event)
{
try
{
return wxFrame::ProcessEvent(event);
}
catch ( const wxChar *msg )
{
wxLogMessage(wxT("Caught a string \"%s\" in MyFrame"), msg);
return true;
}
}
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
// true is to force the frame to close
Close(true);
}
void MyFrame::OnDialog(wxCommandEvent& WXUNUSED(event))
{
try
{
MyDialog dlg(this);
dlg.ShowModal();
}
catch ( ... )
{
wxLogWarning(wxT("An exception in MyDialog"));
Destroy();
throw;
}
}
void MyFrame::OnThrowInt(wxCommandEvent& WXUNUSED(event))
{
throw -17;
}
void MyFrame::OnThrowString(wxCommandEvent& WXUNUSED(event))
{
throw wxT("string thrown from MyFrame");
}
void MyFrame::OnThrowObject(wxCommandEvent& WXUNUSED(event))
{
throw MyException(wxT("Exception thrown from MyFrame"));
}
void MyFrame::OnThrowUnhandled(wxCommandEvent& WXUNUSED(event))
{
throw UnhandledException();
}
void MyFrame::OnCrash(wxCommandEvent& WXUNUSED(event))
{
DoCrash();
}
void MyFrame::OnTrap(wxCommandEvent& WXUNUSED(event))
{
wxTrap();
}
#if wxUSE_ON_FATAL_EXCEPTION
void MyFrame::OnHandleCrash(wxCommandEvent& event)
{
wxHandleFatalExceptions(event.IsChecked());
}
#endif // wxUSE_ON_FATAL_EXCEPTION
void MyFrame::OnShowAssert(wxCommandEvent& WXUNUSED(event))
{
// provoke an assert from wxArrayString
wxArrayString arr;
arr[0];
}
#if wxUSE_THREADS
void MyFrame::OnShowAssertInThread(wxCommandEvent& WXUNUSED(event))
{
class AssertThread : public wxThread
{
public:
AssertThread()
: wxThread(wxTHREAD_JOINABLE)
{
}
protected:
virtual void *Entry()
{
wxFAIL_MSG("Test assert in another thread.");
return 0;
}
};
AssertThread thread;
thread.Create();
thread.Run();
thread.Wait();
}
#endif // wxUSE_THREADS
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
wxString msg;
msg.Printf( wxT("This is the About dialog of the except sample.\n")
wxT("Welcome to %s"), wxVERSION_STRING);
wxMessageBox(msg, wxT("About Except"), wxOK | wxICON_INFORMATION, this);
}
// ============================================================================
// MyDialog implementation
// ============================================================================
MyDialog::MyDialog(wxFrame *parent)
: wxDialog(parent, wxID_ANY, wxString(wxT("Throw exception dialog")))
{
wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
sizerTop->Add(new wxButton(this, Except_ThrowInt, wxT("Throw &int")),
0, wxCENTRE | wxALL, 5);
sizerTop->Add(new wxButton(this, Except_ThrowObject, wxT("Throw &object")),
0, wxCENTRE | wxALL, 5);
sizerTop->Add(new wxButton(this, Except_Crash, wxT("&Crash")),
0, wxCENTRE | wxALL, 5);
sizerTop->Add(new wxButton(this, wxID_CANCEL, wxT("&Cancel")),
0, wxCENTRE | wxALL, 5);
SetSizerAndFit(sizerTop);
}
void MyDialog::OnThrowInt(wxCommandEvent& WXUNUSED(event))
{
throw 17;
}
void MyDialog::OnThrowObject(wxCommandEvent& WXUNUSED(event))
{
throw MyException(wxT("Exception thrown from MyDialog"));
}
void MyDialog::OnCrash(wxCommandEvent& WXUNUSED(event))
{
DoCrash();
}