handle fatal exceptions in the other threads (based on patch 1459813 by Carl-Friedric...
[wxWidgets.git] / src / msw / main.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: msw/main.cpp
3 // Purpose: WinMain/DllMain
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
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 #include "wx/event.h"
28 #include "wx/app.h"
29 #include "wx/cmdline.h"
30 #include "wx/scopeguard.h"
31
32 #include "wx/msw/private.h"
33 #include "wx/msw/seh.h"
34
35 #if wxUSE_ON_FATAL_EXCEPTION
36 #include "wx/datetime.h"
37 #include "wx/msw/crashrpt.h"
38 #endif // wxUSE_ON_FATAL_EXCEPTION
39
40 #ifdef __WXWINCE__
41 // there is no ExitProcess() under CE but exiting the main thread has the
42 // same effect
43 #ifndef ExitProcess
44 #define ExitProcess ExitThread
45 #endif
46 #endif // __WXWINCE__
47
48 #ifdef __BORLANDC__
49 // BC++ has to be special: its run-time expects the DLL entry point to be
50 // named DllEntryPoint instead of the (more) standard DllMain
51 #define DllMain DllEntryPoint
52 #endif // __BORLANDC__
53
54 #if defined(__WXMICROWIN__)
55 #define HINSTANCE HANDLE
56 #endif
57
58 // defined in common/init.cpp
59 extern int wxEntryReal(int& argc, wxChar **argv);
60
61 // ============================================================================
62 // implementation: various entry points
63 // ============================================================================
64
65 #if wxUSE_BASE
66
67 #if wxUSE_ON_FATAL_EXCEPTION && defined(__VISUALC__) && !defined(__WXWINCE__)
68 // VC++ (at least from 4.0 up to version 7.1) is incredibly broken in that
69 // a "catch ( ... )" will *always* catch SEH exceptions in it even though
70 // it should have never been the case... to prevent such catches from
71 // stealing the exceptions from our wxGlobalSEHandler which is only called
72 // if the exception is not handled elsewhere, we have to also call it from
73 // a special SEH translator function which is called by VC CRT when a Win32
74 // exception occurs
75
76 // this warns that /EHa (async exceptions) should be used when using
77 // _set_se_translator but, in fact, this doesn't seem to change anything
78 // with VC++ up to 7.1 -- to be confirmed with VC++ 8
79 #if _MSC_VER <= 1310
80 #pragma warning(disable:4535)
81 #endif
82
83 // note that the SE translator must be called wxSETranslator!
84 #define DisableAutomaticSETranslator() _set_se_translator(wxSETranslator)
85 #else // !__VISUALC__
86 #define DisableAutomaticSETranslator()
87 #endif // __VISUALC__/!__VISUALC__
88
89 // ----------------------------------------------------------------------------
90 // wrapper wxEntry catching all Win32 exceptions occurring in a wx program
91 // ----------------------------------------------------------------------------
92
93 // wrap real wxEntry in a try-except block to be able to call
94 // OnFatalException() if necessary
95 #if wxUSE_ON_FATAL_EXCEPTION
96
97 // global pointer to exception information, only valid inside OnFatalException,
98 // used by wxStackWalker and wxCrashReport
99 extern EXCEPTION_POINTERS *wxGlobalSEInformation = NULL;
100
101 // flag telling us whether the application wants to handle exceptions at all
102 static bool gs_handleExceptions = false;
103
104 static void wxFatalExit()
105 {
106 // use the same exit code as abort()
107 ::ExitProcess(3);
108 }
109
110 unsigned long wxGlobalSEHandler(EXCEPTION_POINTERS *pExcPtrs)
111 {
112 if ( gs_handleExceptions && wxTheApp )
113 {
114 // store the pointer to exception info
115 wxGlobalSEInformation = pExcPtrs;
116
117 // give the user a chance to do something special about this
118 wxSEH_TRY
119 {
120 wxTheApp->OnFatalException();
121 }
122 wxSEH_IGNORE // ignore any exceptions inside the exception handler
123
124 wxGlobalSEInformation = NULL;
125
126 // this will execute our handler and terminate the process
127 return EXCEPTION_EXECUTE_HANDLER;
128 }
129
130 return EXCEPTION_CONTINUE_SEARCH;
131 }
132
133 #ifdef __VISUALC__
134
135 void wxSETranslator(unsigned int WXUNUSED(code), EXCEPTION_POINTERS *ep)
136 {
137 switch ( wxGlobalSEHandler(ep) )
138 {
139 default:
140 wxFAIL_MSG( _T("unexpected wxGlobalSEHandler() return value") );
141 // fall through
142
143 case EXCEPTION_EXECUTE_HANDLER:
144 // if wxApp::OnFatalException() had been called we should exit the
145 // application -- but we shouldn't kill our host when we're a DLL
146 #ifndef WXMAKINGDLL
147 wxFatalExit();
148 #endif // not a DLL
149 break;
150
151 case EXCEPTION_CONTINUE_SEARCH:
152 // we're called for each "catch ( ... )" and if we (re)throw from
153 // here, the catch handler body is not executed, so the effect is
154 // as if had inhibited translation of SE to C++ ones because the
155 // handler will never see any structured exceptions
156 throw;
157 }
158 }
159
160 #endif // __VISUALC__
161
162 bool wxHandleFatalExceptions(bool doit)
163 {
164 // assume this can only be called from the main thread
165 gs_handleExceptions = doit;
166
167 #if wxUSE_CRASHREPORT
168 if ( doit )
169 {
170 // try to find a place where we can put out report file later
171 wxChar fullname[MAX_PATH];
172 if ( !::GetTempPath(WXSIZEOF(fullname), fullname) )
173 {
174 wxLogLastError(_T("GetTempPath"));
175
176 // when all else fails...
177 wxStrcpy(fullname, _T("c:\\"));
178 }
179
180 // use PID and date to make the report file name more unique
181 wxString name = wxString::Format
182 (
183 _T("%s_%s_%lu.dmp"),
184 wxTheApp ? wxTheApp->GetAppName().c_str()
185 : _T("wxwindows"),
186 wxDateTime::Now().Format(_T("%Y%m%dT%H%M%S")).c_str(),
187 ::GetCurrentProcessId()
188 );
189
190 wxStrncat(fullname, name, WXSIZEOF(fullname) - wxStrlen(fullname) - 1);
191
192 wxCrashReport::SetFileName(fullname);
193 }
194 #endif // wxUSE_CRASHREPORT
195
196 return true;
197 }
198
199 int wxEntry(int& argc, wxChar **argv)
200 {
201 DisableAutomaticSETranslator();
202
203 wxSEH_TRY
204 {
205 return wxEntryReal(argc, argv);
206 }
207 wxSEH_HANDLE(-1)
208 }
209
210 #else // !wxUSE_ON_FATAL_EXCEPTION
211
212 #if defined(__VISUALC__) && !defined(__WXWINCE__)
213
214 static void
215 wxSETranslator(unsigned int WXUNUSED(code), EXCEPTION_POINTERS * WXUNUSED(ep))
216 {
217 // see wxSETranslator() version for wxUSE_ON_FATAL_EXCEPTION above
218 throw;
219 }
220
221 #endif // __VISUALC__
222
223 int wxEntry(int& argc, wxChar **argv)
224 {
225 DisableAutomaticSETranslator();
226
227 return wxEntryReal(argc, argv);
228 }
229
230 #endif // wxUSE_ON_FATAL_EXCEPTION/!wxUSE_ON_FATAL_EXCEPTION
231
232 #endif // wxUSE_BASE
233
234 #if wxUSE_GUI && defined(__WXMSW__)
235
236 #if wxUSE_UNICODE && !defined(__WXWINCE__)
237 #define NEED_UNICODE_CHECK
238 #endif
239
240 #ifdef NEED_UNICODE_CHECK
241
242 // check whether Unicode is available
243 static bool wxIsUnicodeAvailable()
244 {
245 static const wchar_t *ERROR_STRING = L"wxWidgets Fatal Error";
246
247 if ( wxGetOsVersion() != wxWINDOWS_NT )
248 {
249 // we need to be built with MSLU support
250 #if !wxUSE_UNICODE_MSLU
251 // note that we can use MessageBoxW() as it's implemented even under
252 // Win9x - OTOH, we can't use wxGetTranslation() because the file APIs
253 // used by wxLocale are not
254 ::MessageBox
255 (
256 NULL,
257 L"This program uses Unicode and requires Windows NT/2000/XP.\n"
258 L"\n"
259 L"Program aborted.",
260 ERROR_STRING,
261 MB_ICONERROR | MB_OK
262 );
263
264 return false;
265 #else // wxUSE_UNICODE_MSLU
266 // and the MSLU DLL must also be available
267 HMODULE hmod = ::LoadLibraryA("unicows.dll");
268 if ( !hmod )
269 {
270 ::MessageBox
271 (
272 NULL,
273 L"This program uses Unicode and requires unicows.dll to work "
274 L"under current operating system.\n"
275 L"\n"
276 L"Please install unicows.dll and relaunch the program.",
277 ERROR_STRING,
278 MB_ICONERROR | MB_OK
279 );
280 return false;
281 }
282
283 // this is not really necessary but be tidy
284 ::FreeLibrary(hmod);
285
286 // finally do the last check: has unicows.lib initialized correctly?
287 hmod = ::LoadLibraryW(L"unicows.dll");
288 if ( !hmod )
289 {
290 ::MessageBox
291 (
292 NULL,
293 L"This program uses Unicode but is not using unicows.dll\n"
294 L"correctly and so cannot work under current operating system.\n"
295 L"Please contact the program author for an updated version.\n"
296 L"\n"
297 L"Program aborted.",
298 ERROR_STRING,
299 MB_ICONERROR | MB_OK
300 );
301
302 return false;
303 }
304
305 ::FreeLibrary(hmod);
306 #endif // !wxUSE_UNICODE_MSLU
307 }
308
309 return true;
310 }
311
312 #endif // NEED_UNICODE_CHECK
313
314 // ----------------------------------------------------------------------------
315 // Windows-specific wxEntry
316 // ----------------------------------------------------------------------------
317
318 // helper function used to clean up in wxEntry() just below
319 //
320 // notice that argv elements are supposed to be allocated using malloc() while
321 // argv array itself is allocated with new
322 static void wxFreeArgs(int argc, wxChar **argv)
323 {
324 for ( int i = 0; i < argc; i++ )
325 {
326 free(argv[i]);
327 }
328
329 delete [] argv;
330 }
331
332 WXDLLEXPORT int wxEntry(HINSTANCE hInstance,
333 HINSTANCE WXUNUSED(hPrevInstance),
334 wxCmdLineArgType WXUNUSED(pCmdLine),
335 int nCmdShow)
336 {
337 // the first thing to do is to check if we're trying to run an Unicode
338 // program under Win9x w/o MSLU emulation layer - if so, abort right now
339 // as it has no chance to work and has all chances to crash
340 #ifdef NEED_UNICODE_CHECK
341 if ( !wxIsUnicodeAvailable() )
342 return -1;
343 #endif // NEED_UNICODE_CHECK
344
345
346 // remember the parameters Windows gave us
347 wxSetInstance(hInstance);
348 wxApp::m_nCmdShow = nCmdShow;
349
350 // parse the command line: we can't use pCmdLine in Unicode build so it is
351 // simpler to never use it at all (this also results in a more correct
352 // argv[0])
353
354 // break the command line in words
355 wxArrayString args;
356
357 const wxChar *cmdLine = ::GetCommandLine();
358 if ( cmdLine )
359 {
360 args = wxCmdLineParser::ConvertStringToArgs(cmdLine);
361 }
362
363 #ifdef __WXWINCE__
364 // WinCE doesn't insert the program itself, so do it ourselves.
365 args.Insert(wxGetFullModuleName(), 0);
366 #endif
367
368 int argc = args.GetCount();
369
370 // +1 here for the terminating NULL
371 wxChar **argv = new wxChar *[argc + 1];
372 for ( int i = 0; i < argc; i++ )
373 {
374 argv[i] = wxStrdup(args[i]);
375 }
376
377 // argv[] must be NULL-terminated
378 argv[argc] = NULL;
379
380 wxON_BLOCK_EXIT2(wxFreeArgs, argc, argv);
381
382 return wxEntry(argc, argv);
383 }
384
385 #endif // wxUSE_GUI && __WXMSW__
386
387 // ----------------------------------------------------------------------------
388 // global HINSTANCE
389 // ----------------------------------------------------------------------------
390
391 #if wxUSE_BASE
392
393 HINSTANCE wxhInstance = 0;
394
395 extern "C" HINSTANCE wxGetInstance()
396 {
397 return wxhInstance;
398 }
399
400 void wxSetInstance(HINSTANCE hInst)
401 {
402 wxhInstance = hInst;
403 }
404
405 #endif // wxUSE_BASE
406