Fixed compile error on non-MSW platforms
[wxWidgets.git] / src / common / appbase.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: common/base/appbase.cpp
3 // Purpose: implements wxAppConsole class
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 19.06.2003 (extracted from common/appcmn.cpp)
7 // RCS-ID: $Id$
8 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9 // License: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // for compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #ifndef WX_PRECOMP
28 #include "wx/app.h"
29 #include "wx/intl.h"
30 #include "wx/list.h"
31 #include "wx/log.h"
32 #endif //WX_PRECOMP
33
34 #include "wx/utils.h"
35 #include "wx/apptrait.h"
36 #include "wx/cmdline.h"
37 #include "wx/confbase.h"
38 #include "wx/filename.h"
39 #include "wx/msgout.h"
40 #include "wx/tokenzr.h"
41
42 #if !defined(__WXMSW__) || defined(__WXMICROWIN__)
43 #include <signal.h> // for SIGTRAP used by wxTrap()
44 #endif //Win/Unix
45
46 #if defined(__WXMSW__)
47 #include "wx/msw/wrapwin.h" // includes windows.h for MessageBox()
48 #endif
49
50 #if wxUSE_FONTMAP
51 #include "wx/fontmap.h"
52 #endif // wxUSE_FONTMAP
53
54 #if defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
55 // For MacTypes.h for Debugger function
56 #include <CoreFoundation/CFBase.h>
57 #endif
58
59 #if defined(__WXMAC__)
60 // VZ: MacTypes.h is enough under Mac OS X (where I could test it) but
61 // I don't know which headers are needed under earlier systems so
62 // include everything when in doubt
63 #ifdef __DARWIN__
64 #include "MacTypes.h"
65 #else
66 #include "wx/mac/private.h" // includes mac headers
67 #endif
68 #endif // __WXMAC__
69
70 #ifdef __WXDEBUG__
71 #ifdef wxUSE_STACKWALKER
72 #include "wx/stackwalk.h"
73 #ifdef __WXMSW__
74 #include "wx/msw/debughlp.h"
75 #endif
76 #endif // wxUSE_STACKWALKER
77 #endif // __WXDEBUG__
78
79 // ----------------------------------------------------------------------------
80 // private functions prototypes
81 // ----------------------------------------------------------------------------
82
83 #ifdef __WXDEBUG__
84 // really just show the assert dialog
85 static bool DoShowAssertDialog(const wxString& msg);
86
87 // prepare for showing the assert dialog, use the given traits or
88 // DoShowAssertDialog() as last fallback to really show it
89 static
90 void ShowAssertDialog(const wxChar *szFile,
91 int nLine,
92 const wxChar *szCond,
93 const wxChar *szMsg,
94 wxAppTraits *traits = NULL);
95
96 // turn on the trace masks specified in the env variable WXTRACE
97 static void LINKAGEMODE SetTraceMasks();
98 #endif // __WXDEBUG__
99
100 // ----------------------------------------------------------------------------
101 // global vars
102 // ----------------------------------------------------------------------------
103
104 wxAppConsole *wxAppConsole::ms_appInstance = NULL;
105
106 wxAppInitializerFunction wxAppConsole::ms_appInitFn = NULL;
107
108 // ============================================================================
109 // wxAppConsole implementation
110 // ============================================================================
111
112 // ----------------------------------------------------------------------------
113 // ctor/dtor
114 // ----------------------------------------------------------------------------
115
116 wxAppConsole::wxAppConsole()
117 {
118 m_traits = NULL;
119
120 ms_appInstance = this;
121
122 #ifdef __WXDEBUG__
123 SetTraceMasks();
124 #if wxUSE_UNICODE
125 // In unicode mode the SetTraceMasks call can cause an apptraits to be
126 // created, but since we are still in the constructor the wrong kind will
127 // be created for GUI apps. Destroy it so it can be created again later.
128 delete m_traits;
129 m_traits = NULL;
130 #endif
131 #endif
132 }
133
134 wxAppConsole::~wxAppConsole()
135 {
136 delete m_traits;
137 }
138
139 // ----------------------------------------------------------------------------
140 // initilization/cleanup
141 // ----------------------------------------------------------------------------
142
143 bool wxAppConsole::Initialize(int& argc, wxChar **argv)
144 {
145 #if wxUSE_LOG
146 // If some code logged something before wxApp instance was created,
147 // wxLogStderr was set as the target. Undo it here by destroying the
148 // current target. It will be re-created next time logging is needed, but
149 // this time wxAppTraits will be used:
150 delete wxLog::SetActiveTarget(NULL);
151 #endif // wxUSE_LOG
152
153 // remember the command line arguments
154 this->argc = argc;
155 this->argv = argv;
156
157 #ifndef __WXPALMOS__
158 if ( m_appName.empty() && argv )
159 {
160 // the application name is, by default, the name of its executable file
161 wxFileName::SplitPath(argv[0], NULL, &m_appName, NULL);
162 }
163 #endif
164
165 return true;
166 }
167
168 void wxAppConsole::CleanUp()
169 {
170 }
171
172 // ----------------------------------------------------------------------------
173 // OnXXX() callbacks
174 // ----------------------------------------------------------------------------
175
176 bool wxAppConsole::OnInit()
177 {
178 #if wxUSE_CMDLINE_PARSER
179 wxCmdLineParser parser(argc, argv);
180
181 OnInitCmdLine(parser);
182
183 bool cont;
184 switch ( parser.Parse(false /* don't show usage */) )
185 {
186 case -1:
187 cont = OnCmdLineHelp(parser);
188 break;
189
190 case 0:
191 cont = OnCmdLineParsed(parser);
192 break;
193
194 default:
195 cont = OnCmdLineError(parser);
196 break;
197 }
198
199 if ( !cont )
200 return false;
201 #endif // wxUSE_CMDLINE_PARSER
202
203 return true;
204 }
205
206 int wxAppConsole::OnExit()
207 {
208 #if wxUSE_CONFIG
209 // delete the config object if any (don't use Get() here, but Set()
210 // because Get() could create a new config object)
211 delete wxConfigBase::Set((wxConfigBase *) NULL);
212 #endif // wxUSE_CONFIG
213
214 // use Set(NULL) and not Get() to avoid creating a message output object on
215 // demand when we just want to delete it
216 delete wxMessageOutput::Set(NULL);
217
218 return 0;
219 }
220
221 void wxAppConsole::Exit()
222 {
223 exit(-1);
224 }
225
226 // ----------------------------------------------------------------------------
227 // traits stuff
228 // ----------------------------------------------------------------------------
229
230 wxAppTraits *wxAppConsole::CreateTraits()
231 {
232 return new wxConsoleAppTraits;
233 }
234
235 wxAppTraits *wxAppConsole::GetTraits()
236 {
237 // FIXME-MT: protect this with a CS?
238 if ( !m_traits )
239 {
240 m_traits = CreateTraits();
241
242 wxASSERT_MSG( m_traits, _T("wxApp::CreateTraits() failed?") );
243 }
244
245 return m_traits;
246 }
247
248 // we must implement CreateXXX() in wxApp itself for backwards compatibility
249 #if WXWIN_COMPATIBILITY_2_4
250
251 #if wxUSE_LOG
252
253 wxLog *wxAppConsole::CreateLogTarget()
254 {
255 wxAppTraits *traits = GetTraits();
256 return traits ? traits->CreateLogTarget() : NULL;
257 }
258
259 #endif // wxUSE_LOG
260
261 wxMessageOutput *wxAppConsole::CreateMessageOutput()
262 {
263 wxAppTraits *traits = GetTraits();
264 return traits ? traits->CreateMessageOutput() : NULL;
265 }
266
267 #endif // WXWIN_COMPATIBILITY_2_4
268
269 // ----------------------------------------------------------------------------
270 // event processing
271 // ----------------------------------------------------------------------------
272
273 void wxAppConsole::ProcessPendingEvents()
274 {
275 #if wxUSE_THREADS
276 if ( !wxPendingEventsLocker )
277 return;
278 #endif
279
280 // ensure that we're the only thread to modify the pending events list
281 wxENTER_CRIT_SECT( *wxPendingEventsLocker );
282
283 if ( !wxPendingEvents )
284 {
285 wxLEAVE_CRIT_SECT( *wxPendingEventsLocker );
286 return;
287 }
288
289 // iterate until the list becomes empty
290 wxList::compatibility_iterator node = wxPendingEvents->GetFirst();
291 while (node)
292 {
293 wxEvtHandler *handler = (wxEvtHandler *)node->GetData();
294 wxPendingEvents->Erase(node);
295
296 // In ProcessPendingEvents(), new handlers might be add
297 // and we can safely leave the critical section here.
298 wxLEAVE_CRIT_SECT( *wxPendingEventsLocker );
299
300 handler->ProcessPendingEvents();
301
302 wxENTER_CRIT_SECT( *wxPendingEventsLocker );
303
304 node = wxPendingEvents->GetFirst();
305 }
306
307 wxLEAVE_CRIT_SECT( *wxPendingEventsLocker );
308 }
309
310 int wxAppConsole::FilterEvent(wxEvent& WXUNUSED(event))
311 {
312 // process the events normally by default
313 return -1;
314 }
315
316 // ----------------------------------------------------------------------------
317 // exception handling
318 // ----------------------------------------------------------------------------
319
320 #if wxUSE_EXCEPTIONS
321
322 void
323 wxAppConsole::HandleEvent(wxEvtHandler *handler,
324 wxEventFunction func,
325 wxEvent& event) const
326 {
327 // by default, simply call the handler
328 (handler->*func)(event);
329 }
330
331 bool
332 wxAppConsole::OnExceptionInMainLoop()
333 {
334 throw;
335
336 // some compilers are too stupid to know that we never return after throw
337 #if defined(__DMC__) || (defined(_MSC_VER) && _MSC_VER < 1200)
338 return false;
339 #endif
340 }
341
342 #endif // wxUSE_EXCEPTIONS
343
344 // ----------------------------------------------------------------------------
345 // cmd line parsing
346 // ----------------------------------------------------------------------------
347
348 #if wxUSE_CMDLINE_PARSER
349
350 #define OPTION_VERBOSE _T("verbose")
351
352 void wxAppConsole::OnInitCmdLine(wxCmdLineParser& parser)
353 {
354 // the standard command line options
355 static const wxCmdLineEntryDesc cmdLineDesc[] =
356 {
357 {
358 wxCMD_LINE_SWITCH,
359 _T("h"),
360 _T("help"),
361 gettext_noop("show this help message"),
362 wxCMD_LINE_VAL_NONE,
363 wxCMD_LINE_OPTION_HELP
364 },
365
366 #if wxUSE_LOG
367 {
368 wxCMD_LINE_SWITCH,
369 wxEmptyString,
370 OPTION_VERBOSE,
371 gettext_noop("generate verbose log messages"),
372 wxCMD_LINE_VAL_NONE,
373 0x0
374 },
375 #endif // wxUSE_LOG
376
377 // terminator
378 {
379 wxCMD_LINE_NONE,
380 wxEmptyString,
381 wxEmptyString,
382 wxEmptyString,
383 wxCMD_LINE_VAL_NONE,
384 0x0
385 }
386 };
387
388 parser.SetDesc(cmdLineDesc);
389 }
390
391 bool wxAppConsole::OnCmdLineParsed(wxCmdLineParser& parser)
392 {
393 #if wxUSE_LOG
394 if ( parser.Found(OPTION_VERBOSE) )
395 {
396 wxLog::SetVerbose(true);
397 }
398 #else
399 wxUnusedVar(parser);
400 #endif // wxUSE_LOG
401
402 return true;
403 }
404
405 bool wxAppConsole::OnCmdLineHelp(wxCmdLineParser& parser)
406 {
407 parser.Usage();
408
409 return false;
410 }
411
412 bool wxAppConsole::OnCmdLineError(wxCmdLineParser& parser)
413 {
414 parser.Usage();
415
416 return false;
417 }
418
419 #endif // wxUSE_CMDLINE_PARSER
420
421 // ----------------------------------------------------------------------------
422 // debugging support
423 // ----------------------------------------------------------------------------
424
425 /* static */
426 bool wxAppConsole::CheckBuildOptions(const char *optionsSignature,
427 const char *componentName)
428 {
429 #if 0 // can't use wxLogTrace, not up and running yet
430 printf("checking build options object '%s' (ptr %p) in '%s'\n",
431 optionsSignature, optionsSignature, componentName);
432 #endif
433
434 if ( strcmp(optionsSignature, WX_BUILD_OPTIONS_SIGNATURE) != 0 )
435 {
436 wxString lib = wxString::FromAscii(WX_BUILD_OPTIONS_SIGNATURE);
437 wxString prog = wxString::FromAscii(optionsSignature);
438 wxString progName = wxString::FromAscii(componentName);
439 wxString msg;
440
441 msg.Printf(_T("Mismatch between the program and library build versions detected.\nThe library used %s,\nand %s used %s."),
442 lib.c_str(), progName.c_str(), prog.c_str());
443
444 wxLogFatalError(msg.c_str());
445
446 // normally wxLogFatalError doesn't return
447 return false;
448 }
449 #undef wxCMP
450
451 return true;
452 }
453
454 #ifdef __WXDEBUG__
455
456 void wxAppConsole::OnAssert(const wxChar *file,
457 int line,
458 const wxChar *cond,
459 const wxChar *msg)
460 {
461 ShowAssertDialog(file, line, cond, msg, GetTraits());
462 }
463
464 #endif // __WXDEBUG__
465
466 #if WXWIN_COMPATIBILITY_2_4
467
468 bool wxAppConsole::CheckBuildOptions(const wxBuildOptions& buildOptions)
469 {
470 return CheckBuildOptions(buildOptions.m_signature, "your program");
471 }
472
473 #endif
474
475 // ============================================================================
476 // other classes implementations
477 // ============================================================================
478
479 // ----------------------------------------------------------------------------
480 // wxConsoleAppTraitsBase
481 // ----------------------------------------------------------------------------
482
483 #if wxUSE_LOG
484
485 wxLog *wxConsoleAppTraitsBase::CreateLogTarget()
486 {
487 return new wxLogStderr;
488 }
489
490 #endif // wxUSE_LOG
491
492 wxMessageOutput *wxConsoleAppTraitsBase::CreateMessageOutput()
493 {
494 return new wxMessageOutputStderr;
495 }
496
497 #if wxUSE_FONTMAP
498
499 wxFontMapper *wxConsoleAppTraitsBase::CreateFontMapper()
500 {
501 return (wxFontMapper *)new wxFontMapperBase;
502 }
503
504 #endif // wxUSE_FONTMAP
505
506 wxRendererNative *wxConsoleAppTraitsBase::CreateRenderer()
507 {
508 // console applications don't use renderers
509 return NULL;
510 }
511
512 #ifdef __WXDEBUG__
513 bool wxConsoleAppTraitsBase::ShowAssertDialog(const wxString& msg)
514 {
515 return wxAppTraitsBase::ShowAssertDialog(msg);
516 }
517 #endif
518
519 bool wxConsoleAppTraitsBase::HasStderr()
520 {
521 // console applications always have stderr, even under Mac/Windows
522 return true;
523 }
524
525 void wxConsoleAppTraitsBase::ScheduleForDestroy(wxObject *object)
526 {
527 delete object;
528 }
529
530 void wxConsoleAppTraitsBase::RemoveFromPendingDelete(wxObject * WXUNUSED(object))
531 {
532 // nothing to do
533 }
534
535 #if wxUSE_SOCKETS
536 GSocketGUIFunctionsTable* wxConsoleAppTraitsBase::GetSocketGUIFunctionsTable()
537 {
538 return NULL;
539 }
540 #endif
541
542 // ----------------------------------------------------------------------------
543 // wxAppTraits
544 // ----------------------------------------------------------------------------
545
546 #ifdef __WXDEBUG__
547
548 bool wxAppTraitsBase::ShowAssertDialog(const wxString& msg)
549 {
550 return DoShowAssertDialog(msg);
551 }
552
553 #endif // __WXDEBUG__
554
555 // ============================================================================
556 // global functions implementation
557 // ============================================================================
558
559 void wxExit()
560 {
561 if ( wxTheApp )
562 {
563 wxTheApp->Exit();
564 }
565 else
566 {
567 // what else can we do?
568 exit(-1);
569 }
570 }
571
572 void wxWakeUpIdle()
573 {
574 if ( wxTheApp )
575 {
576 wxTheApp->WakeUpIdle();
577 }
578 //else: do nothing, what can we do?
579 }
580
581 #ifdef __WXDEBUG__
582
583 // wxASSERT() helper
584 bool wxAssertIsEqual(int x, int y)
585 {
586 return x == y;
587 }
588
589 // break into the debugger
590 void wxTrap()
591 {
592 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
593 DebugBreak();
594 #elif defined(__WXMAC__) && !defined(__DARWIN__)
595 #if __powerc
596 Debugger();
597 #else
598 SysBreak();
599 #endif
600 #elif defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
601 Debugger();
602 #elif defined(__UNIX__)
603 raise(SIGTRAP);
604 #else
605 // TODO
606 #endif // Win/Unix
607 }
608
609 void wxAssert(int cond,
610 const wxChar *szFile,
611 int nLine,
612 const wxChar *szCond,
613 const wxChar *szMsg)
614 {
615 if ( !cond )
616 wxOnAssert(szFile, nLine, szCond, szMsg);
617 }
618
619 // this function is called when an assert fails
620 void wxOnAssert(const wxChar *szFile,
621 int nLine,
622 const wxChar *szCond,
623 const wxChar *szMsg)
624 {
625 // FIXME MT-unsafe
626 static bool s_bInAssert = false;
627
628 if ( s_bInAssert )
629 {
630 // He-e-e-e-elp!! we're trapped in endless loop
631 wxTrap();
632
633 s_bInAssert = false;
634
635 return;
636 }
637
638 s_bInAssert = true;
639
640 if ( !wxTheApp )
641 {
642 // by default, show the assert dialog box -- we can't customize this
643 // behaviour
644 ShowAssertDialog(szFile, nLine, szCond, szMsg);
645 }
646 else
647 {
648 // let the app process it as it wants
649 wxTheApp->OnAssert(szFile, nLine, szCond, szMsg);
650 }
651
652 s_bInAssert = false;
653 }
654
655 #endif // __WXDEBUG__
656
657 // ============================================================================
658 // private functions implementation
659 // ============================================================================
660
661 #ifdef __WXDEBUG__
662
663 static void LINKAGEMODE SetTraceMasks()
664 {
665 #if wxUSE_LOG
666 wxString mask;
667 if ( wxGetEnv(wxT("WXTRACE"), &mask) )
668 {
669 wxStringTokenizer tkn(mask, wxT(",;:"));
670 while ( tkn.HasMoreTokens() )
671 wxLog::AddTraceMask(tkn.GetNextToken());
672 }
673 #endif // wxUSE_LOG
674 }
675
676 bool DoShowAssertDialog(const wxString& msg)
677 {
678 // under MSW we can show the dialog even in the console mode
679 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
680 wxString msgDlg(msg);
681
682 // this message is intentionally not translated -- it is for
683 // developpers only
684 msgDlg += wxT("\nDo you want to stop the program?\n")
685 wxT("You can also choose [Cancel] to suppress ")
686 wxT("further warnings.");
687
688 switch ( ::MessageBox(NULL, msgDlg, _T("wxWidgets Debug Alert"),
689 MB_YESNOCANCEL | MB_ICONSTOP ) )
690 {
691 case IDYES:
692 wxTrap();
693 break;
694
695 case IDCANCEL:
696 // stop the asserts
697 return true;
698
699 //case IDNO: nothing to do
700 }
701 #else // !__WXMSW__
702 wxFprintf(stderr, wxT("%s\n"), msg.c_str());
703 fflush(stderr);
704
705 // TODO: ask the user to enter "Y" or "N" on the console?
706 wxTrap();
707 #endif // __WXMSW__/!__WXMSW__
708
709 // continue with the asserts
710 return false;
711 }
712
713 static wxString GetAssertStackTrace()
714 {
715 wxString stackTrace;
716
717 #ifdef __WXMSW__
718 // check that we can get the stack trace before trying to do it
719 if ( !wxDbgHelpDLL::Init() )
720 return stackTrace;
721 #endif
722
723 class StackDump : public wxStackWalker
724 {
725 public:
726 StackDump() { }
727
728 const wxString& GetStackTrace() const { return m_stackTrace; }
729
730 protected:
731 virtual void OnStackFrame(const wxStackFrame& frame)
732 {
733 m_stackTrace << wxString::Format(_T("[%02d] "), frame.GetLevel());
734
735 wxString name = frame.GetName();
736 if ( !name.empty() )
737 {
738 m_stackTrace << wxString::Format(_T("%-40s"), name.c_str());
739 }
740 else
741 {
742 m_stackTrace << wxString::Format
743 (
744 _T("0x%08lx"),
745 (unsigned long)frame.GetAddress()
746 );
747 }
748
749 if ( frame.HasSourceLocation() )
750 {
751 m_stackTrace << _T('\t')
752 << frame.GetFileName()
753 << _T(':')
754 << frame.GetLine();
755 }
756
757 m_stackTrace << _T('\n');
758 }
759
760 private:
761 wxString m_stackTrace;
762 };
763
764 StackDump dump;
765 dump.Walk(5); // don't show OnAssert() call itself
766 stackTrace = dump.GetStackTrace();
767
768 // don't show more than maxLines or we could get a dialog too tall to be
769 // shown on screen: 20 should be ok everywhere as even with 15 pixel high
770 // characters it is still only 300 pixels...
771 static const int maxLines = 20;
772 const int count = stackTrace.Freq(wxT('\n'));
773 for ( int i = 0; i < count - maxLines; i++ )
774 stackTrace = stackTrace.BeforeLast(wxT('\n'));
775
776 return stackTrace;
777 }
778
779 // show the assert modal dialog
780 static
781 void ShowAssertDialog(const wxChar *szFile,
782 int nLine,
783 const wxChar *szCond,
784 const wxChar *szMsg,
785 wxAppTraits *traits)
786 {
787 // this variable can be set to true to suppress "assert failure" messages
788 static bool s_bNoAsserts = false;
789
790 wxString msg;
791 msg.reserve(2048);
792
793 // make life easier for people using VC++ IDE by using this format: like
794 // this, clicking on the message will take us immediately to the place of
795 // the failed assert
796 msg.Printf(wxT("%s(%d): assert \"%s\" failed"), szFile, nLine, szCond);
797
798 if ( szMsg )
799 {
800 msg << _T(": ") << szMsg;
801 }
802 else // no message given
803 {
804 msg << _T('.');
805 }
806
807 #if wxUSE_STACKWALKER
808 const wxString stackTrace = GetAssertStackTrace();
809 if ( !stackTrace.empty() )
810 {
811 msg << _T("\n\nCall stack:\n") << stackTrace;
812 }
813 #endif // wxUSE_STACKWALKER
814
815 #if wxUSE_THREADS
816 // if we are not in the main thread, output the assert directly and trap
817 // since dialogs cannot be displayed
818 if ( !wxThread::IsMain() )
819 {
820 msg += wxT(" [in child thread]");
821
822 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
823 msg << wxT("\r\n");
824 OutputDebugString(msg );
825 #else
826 // send to stderr
827 wxFprintf(stderr, wxT("%s\n"), msg.c_str());
828 fflush(stderr);
829 #endif
830 // He-e-e-e-elp!! we're asserting in a child thread
831 wxTrap();
832 }
833 else
834 #endif // wxUSE_THREADS
835
836 if ( !s_bNoAsserts )
837 {
838 // send it to the normal log destination
839 wxLogDebug(_T("%s"), msg.c_str());
840
841 if ( traits )
842 {
843 // delegate showing assert dialog (if possible) to that class
844 s_bNoAsserts = traits->ShowAssertDialog(msg);
845 }
846 else // no traits object
847 {
848 // fall back to the function of last resort
849 s_bNoAsserts = DoShowAssertDialog(msg);
850 }
851 }
852 }
853
854 #endif // __WXDEBUG__
855