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