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