55fd62c1e3
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
550 lines
16 KiB
C++
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();
|
|
}
|
|
|