]> git.saurik.com Git - wxWidgets.git/blame - src/msw/app.cpp
Ignore WM_CLOSE generated by wxMSW edit control when Escape is pressed.
[wxWidgets.git] / src / msw / app.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
a71d815b 2// Name: src/msw/app.cpp
2bda0e17
KB
3// Purpose: wxApp
4// Author: Julian Smart
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
6c9a19aa 8// Copyright: (c) Julian Smart
65571936 9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
e5c0b16a
VZ
12// ===========================================================================
13// declarations
14// ===========================================================================
15
16// ---------------------------------------------------------------------------
17// headers
18// ---------------------------------------------------------------------------
19
2bda0e17 20// For compilers that support precompilation, includes "wx.h".
2bda0e17
KB
21#include "wx/wxprec.h"
22
23#if defined(__BORLANDC__)
e5c0b16a 24 #pragma hdrstop
2bda0e17
KB
25#endif
26
27#ifndef WX_PRECOMP
57bd4c60 28 #include "wx/msw/wrapcctl.h"
ad9835c9 29 #include "wx/dynarray.h"
e5c0b16a
VZ
30 #include "wx/frame.h"
31 #include "wx/app.h"
32 #include "wx/utils.h"
33 #include "wx/gdicmn.h"
34 #include "wx/pen.h"
35 #include "wx/brush.h"
36 #include "wx/cursor.h"
37 #include "wx/icon.h"
38 #include "wx/palette.h"
39 #include "wx/dc.h"
40 #include "wx/dialog.h"
41 #include "wx/msgdlg.h"
42 #include "wx/intl.h"
3a3dde0d 43 #include "wx/crt.h"
31f6de22 44 #include "wx/log.h"
02761f6c 45 #include "wx/module.h"
2bda0e17
KB
46#endif
47
e2478fde 48#include "wx/apptrait.h"
7104f65d 49#include "wx/filename.h"
9fc6c21c 50#include "wx/dynlib.h"
031943ac 51#include "wx/evtloop.h"
204abcd4 52#include "wx/thread.h"
fcf92c3a 53#include "wx/scopeguard.h"
d9698bd4 54#include "wx/vector.h"
4bf78aae 55
4286a5b5 56#include "wx/msw/private.h"
025f7d77 57#include "wx/msw/dc.h"
360ae33f 58#include "wx/msw/ole/oleutils.h"
c2ca375c 59#include "wx/msw/private/timer.h"
4286a5b5 60
8614c467
VZ
61#if wxUSE_TOOLTIPS
62 #include "wx/tooltip.h"
63#endif // wxUSE_TOOLTIPS
64
c42404a5
VZ
65// OLE is used for drag-and-drop, clipboard, OLE Automation..., but some
66// compilers don't support it (missing headers, libs, ...)
f172cb82 67#if defined(__GNUWIN32_OLD__) || defined(__SYMANTEC__)
e5c0b16a
VZ
68 #undef wxUSE_OLE
69
70 #define wxUSE_OLE 0
71#endif // broken compilers
72
afafd942 73#if defined(__POCKETPC__) || defined(__SMARTPHONE__)
58b76be1
VZ
74 #include <ole2.h>
75 #include <aygshell.h>
afafd942
JS
76#endif
77
e5c0b16a 78#if wxUSE_OLE
6e0d9d43 79 #include <ole2.h>
d05237ea 80#endif
ce3ed50d 81
2bda0e17 82#include <string.h>
a5e0e655 83#include <ctype.h>
2bda0e17 84
261fb041 85#include "wx/msw/missing.h"
6046e57a 86
25a11614
VZ
87// instead of including <shlwapi.h> which is not part of the core SDK and not
88// shipped at all with other compilers, we always define the parts of it we
89// need here ourselves
90//
91// NB: DLLVER_PLATFORM_WINDOWS will be defined if shlwapi.h had been somehow
92// included already
93#ifndef DLLVER_PLATFORM_WINDOWS
94 // hopefully we don't need to change packing as DWORDs should be already
95 // correctly aligned
96 struct DLLVERSIONINFO
97 {
98 DWORD cbSize;
99 DWORD dwMajorVersion; // Major version
100 DWORD dwMinorVersion; // Minor version
101 DWORD dwBuildNumber; // Build number
102 DWORD dwPlatformID; // DLLVER_PLATFORM_*
103 };
104
105 typedef HRESULT (CALLBACK* DLLGETVERSIONPROC)(DLLVERSIONINFO *);
106#endif // defined(DLLVERSIONINFO)
107
05b4b8ee
VZ
108#ifndef ATTACH_PARENT_PROCESS
109 #define ATTACH_PARENT_PROCESS ((DWORD)-1)
110#endif
238a6044 111
e5c0b16a
VZ
112// ---------------------------------------------------------------------------
113// global variables
114// ---------------------------------------------------------------------------
115
4676948b 116#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
2bda0e17 117extern void wxSetKeyboardHook(bool doIt);
04ef50df 118#endif
2bda0e17 119
c76fb545
VZ
120// because of mingw32 4.3 bug this struct can't be inside the namespace below:
121// see http://article.gmane.org/gmane.comp.lib.wxwidgets.devel/110282
d9698bd4
VZ
122struct ClassRegInfo
123{
563cf28f
VZ
124 ClassRegInfo(const wxChar *name)
125 : regname(name),
126 regnameNR(regname + wxApp::GetNoRedrawClassSuffix())
127 {
128 }
d9698bd4
VZ
129
130 // the name of the registered class with and without CS_[HV]REDRAW styles
9106ea70
VZ
131 wxString regname;
132 wxString regnameNR;
d9698bd4
VZ
133};
134
c76fb545
VZ
135namespace
136{
137
d9698bd4
VZ
138wxVector<ClassRegInfo> gs_regClassesInfo;
139
140} // anonymous namespace
2bda0e17 141
94826170
VZ
142// ----------------------------------------------------------------------------
143// private functions
144// ----------------------------------------------------------------------------
2bda0e17 145
94826170 146LRESULT WXDLLEXPORT APIENTRY wxWndProc(HWND, UINT, WPARAM, LPARAM);
3b415ba4 147
e5c0b16a 148// ===========================================================================
e2478fde
VZ
149// wxGUIAppTraits implementation
150// ===========================================================================
151
152// private class which we use to pass parameters from BeforeChildWaitLoop() to
153// AfterChildWaitLoop()
154struct ChildWaitLoopData
155{
156 ChildWaitLoopData(wxWindowDisabler *wd_, wxWindow *winActive_)
157 {
158 wd = wd_;
159 winActive = winActive_;
160 }
161
162 wxWindowDisabler *wd;
163 wxWindow *winActive;
164};
165
166void *wxGUIAppTraits::BeforeChildWaitLoop()
167{
168 /*
169 We use a dirty hack here to disable all application windows (which we
170 must do because otherwise the calls to wxYield() could lead to some very
171 unexpected reentrancies in the users code) but to avoid losing
172 focus/activation entirely when the child process terminates which would
173 happen if we simply disabled everything using wxWindowDisabler. Indeed,
174 remember that Windows will never activate a disabled window and when the
175 last childs window is closed and Windows looks for a window to activate
176 all our windows are still disabled. There is no way to enable them in
177 time because we don't know when the childs windows are going to be
178 closed, so the solution we use here is to keep one special tiny frame
179 enabled all the time. Then when the child terminates it will get
180 activated and when we close it below -- after reenabling all the other
181 windows! -- the previously active window becomes activated again and
182 everything is ok.
183 */
184 wxBeginBusyCursor();
185
186 // first disable all existing windows
187 wxWindowDisabler *wd = new wxWindowDisabler;
188
189 // then create an "invisible" frame: it has minimal size, is positioned
190 // (hopefully) outside the screen and doesn't appear on the taskbar
191 wxWindow *winActive = new wxFrame
192 (
193 wxTheApp->GetTopWindow(),
fda7962d
JS
194 wxID_ANY,
195 wxEmptyString,
e2478fde
VZ
196 wxPoint(32600, 32600),
197 wxSize(1, 1),
198 wxDEFAULT_FRAME_STYLE | wxFRAME_NO_TASKBAR
199 );
200 winActive->Show();
201
202 return new ChildWaitLoopData(wd, winActive);
203}
204
e2478fde
VZ
205void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig)
206{
207 wxEndBusyCursor();
208
0588f8d7 209 ChildWaitLoopData * const data = (ChildWaitLoopData *)dataOrig;
e2478fde
VZ
210
211 delete data->wd;
212
213 // finally delete the dummy frame and, as wd has been already destroyed and
214 // the other windows reenabled, the activation is going to return to the
215 // window which had had it before
216 data->winActive->Destroy();
51036b44
VZ
217
218 // also delete the temporary data object itself
219 delete data;
e2478fde
VZ
220}
221
dd1af40c 222#if wxUSE_THREADS
e2478fde
VZ
223bool wxGUIAppTraits::DoMessageFromThreadWait()
224{
1bf77ee5
VZ
225 // we should return false only if the app should exit, i.e. only if
226 // Dispatch() determines that the main event loop should terminate
2ddff00c 227 wxEventLoopBase * const evtLoop = wxEventLoop::GetActive();
031943ac
VZ
228 if ( !evtLoop || !evtLoop->Pending() )
229 {
230 // no events means no quit event
231 return true;
232 }
233
234 return evtLoop->Dispatch();
e2478fde
VZ
235}
236
b95a7c31 237DWORD wxGUIAppTraits::WaitForThread(WXHANDLE hThread, int flags)
e570a44b 238{
ef0fdf39
VZ
239 // We only ever dispatch messages from the main thread and, additionally,
240 // even from the main thread we shouldn't wait for the message if we don't
241 // have a running event loop as we would never remove them from the message
242 // queue then and so we would enter an infinite loop as
243 // MsgWaitForMultipleObjects() keeps returning WAIT_OBJECT_0 + 1.
b95a7c31
VZ
244 if ( flags == wxTHREAD_WAIT_BLOCK ||
245 !wxIsMainThread() ||
246 !wxEventLoop::GetActive() )
247 {
248 // Simple blocking wait.
535920ff 249 return DoSimpleWaitForThread(hThread);
b95a7c31 250 }
535920ff 251
e570a44b
VZ
252 return ::MsgWaitForMultipleObjects
253 (
254 1, // number of objects to wait for
255 (HANDLE *)&hThread, // the objects
256 false, // wait for any objects, not all
257 INFINITE, // no timeout
261fb041
WS
258 QS_ALLINPUT | // return as soon as there are any events
259 QS_ALLPOSTMESSAGE
e570a44b
VZ
260 );
261}
dd1af40c 262#endif // wxUSE_THREADS
e570a44b 263
8bb6b2c0 264wxPortId wxGUIAppTraits::GetToolkitVersion(int *majVer, int *minVer) const
a8eaaeb2 265{
8bb6b2c0
VZ
266 OSVERSIONINFO info;
267 wxZeroMemory(info);
268
269 // on Windows, the toolkit version is the same of the OS version
270 // as Windows integrates the OS kernel with the GUI toolkit.
271 info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
272 if ( ::GetVersionEx(&info) )
273 {
274 if ( majVer )
275 *majVer = info.dwMajorVersion;
276 if ( minVer )
277 *minVer = info.dwMinorVersion;
278 }
279
280#if defined(__WXHANDHELD__) || defined(__WXWINCE__)
281 return wxPORT_WINCE;
282#else
283 return wxPORT_MSW;
a8eaaeb2 284#endif
a8eaaeb2
VS
285}
286
a8ff046b
VZ
287#if wxUSE_TIMER
288
b46b1d59 289wxTimerImpl *wxGUIAppTraits::CreateTimerImpl(wxTimer *timer)
c2ca375c 290{
b46b1d59
VZ
291 return new wxMSWTimerImpl(timer);
292}
293
a8ff046b
VZ
294#endif // wxUSE_TIMER
295
2ddff00c 296wxEventLoopBase* wxGUIAppTraits::CreateEventLoop()
b46b1d59
VZ
297{
298 return new wxEventLoop;
5d262fdd 299}
c2ca375c 300
784ee7d5
VZ
301// ---------------------------------------------------------------------------
302// Stuff for using console from the GUI applications
303// ---------------------------------------------------------------------------
304
305#ifndef __WXWINCE__
306
14ca3a3b
VZ
307#if wxUSE_DYNLIB_CLASS
308
784ee7d5
VZ
309#include <wx/dynlib.h>
310
311namespace
312{
313
314/*
315 Helper class to manipulate console from a GUI app.
316
317 Notice that console output is available in the GUI app only if:
318 - AttachConsole() returns TRUE (which means it never works under pre-XP)
319 - we have a valid STD_ERROR_HANDLE
320 - command history hasn't been changed since our startup
321
322 To check if all these conditions are verified, you need to simple call
323 IsOkToUse(). It will check the first two conditions above the first time it
324 is called (and if this fails, the subsequent calls will return immediately)
325 and also recheck the last one every time it is called.
326 */
327class wxConsoleStderr
328{
329public:
330 // default ctor does nothing, call Init() before using this class
331 wxConsoleStderr()
332 {
333 m_hStderr = INVALID_HANDLE_VALUE;
334 m_historyLen =
335 m_dataLen =
336 m_dataLine = 0;
337
338 m_ok = -1;
339 }
340
341 ~wxConsoleStderr()
342 {
343 if ( m_hStderr != INVALID_HANDLE_VALUE )
344 {
345 if ( !::FreeConsole() )
346 {
9a83f860 347 wxLogLastError(wxT("FreeConsole"));
784ee7d5
VZ
348 }
349 }
350 }
351
352 // return true if we were successfully initialized and there had been no
353 // console activity which would interfere with our output since then
354 bool IsOkToUse() const
355 {
356 if ( m_ok == -1 )
357 {
5c33522f 358 wxConsoleStderr * const self = const_cast<wxConsoleStderr *>(this);
784ee7d5
VZ
359 self->m_ok = self->DoInit();
360
361 // no need to call IsHistoryUnchanged() as we just initialized
362 // m_history anyhow
363 return m_ok == 1;
364 }
365
366 return m_ok && IsHistoryUnchanged();
367 }
368
369
370 // output the provided text on the console, return true if ok
371 bool Write(const wxString& text);
372
373private:
374 // called by Init() once only to do the real initialization
375 bool DoInit();
376
377 // retrieve the command line history into the provided buffer and return
378 // its length
379 int GetCommandHistory(wxWxCharBuffer& buf) const;
380
381 // check if the console history has changed
382 bool IsHistoryUnchanged() const;
383
384 int m_ok; // initially -1, set to true or false by Init()
385
386 wxDynamicLibrary m_dllKernel32;
387
388 HANDLE m_hStderr; // console handle, if it's valid we must call
389 // FreeConsole() (even if m_ok != 1)
390
391 wxWxCharBuffer m_history; // command history on startup
392 int m_historyLen; // length command history buffer
393
394 wxCharBuffer m_data; // data between empty line and cursor position
395 int m_dataLen; // length data buffer
396 int m_dataLine; // line offset
397
398 typedef DWORD (WINAPI *GetConsoleCommandHistory_t)(LPTSTR sCommands,
399 DWORD nBufferLength,
400 LPCTSTR sExeName);
401 typedef DWORD (WINAPI *GetConsoleCommandHistoryLength_t)(LPCTSTR sExeName);
402
403 GetConsoleCommandHistory_t m_pfnGetConsoleCommandHistory;
404 GetConsoleCommandHistoryLength_t m_pfnGetConsoleCommandHistoryLength;
405
c0c133e1 406 wxDECLARE_NO_COPY_CLASS(wxConsoleStderr);
784ee7d5
VZ
407};
408
409bool wxConsoleStderr::DoInit()
410{
411 HANDLE hStderr = ::GetStdHandle(STD_ERROR_HANDLE);
412
413 if ( hStderr == INVALID_HANDLE_VALUE || !hStderr )
414 return false;
415
9a83f860 416 if ( !m_dllKernel32.Load(wxT("kernel32.dll")) )
784ee7d5
VZ
417 return false;
418
419 typedef BOOL (WINAPI *AttachConsole_t)(DWORD dwProcessId);
420 AttachConsole_t wxDL_INIT_FUNC(pfn, AttachConsole, m_dllKernel32);
421
422 if ( !pfnAttachConsole || !pfnAttachConsole(ATTACH_PARENT_PROCESS) )
423 return false;
424
425 // console attached, set m_hStderr now to ensure that we free it in the
426 // dtor
427 m_hStderr = hStderr;
428
429 wxDL_INIT_FUNC_AW(m_pfn, GetConsoleCommandHistory, m_dllKernel32);
430 if ( !m_pfnGetConsoleCommandHistory )
431 return false;
432
433 wxDL_INIT_FUNC_AW(m_pfn, GetConsoleCommandHistoryLength, m_dllKernel32);
434 if ( !m_pfnGetConsoleCommandHistoryLength )
435 return false;
436
437 // remember the current command history to be able to compare with it later
438 // in IsHistoryUnchanged()
439 m_historyLen = GetCommandHistory(m_history);
440 if ( !m_history )
441 return false;
442
443
444 // now find the first blank line above the current position
445 CONSOLE_SCREEN_BUFFER_INFO csbi;
446
447 if ( !::GetConsoleScreenBufferInfo(m_hStderr, &csbi) )
448 {
9a83f860 449 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
784ee7d5
VZ
450 return false;
451 }
452
453 COORD pos;
454 pos.X = 0;
455 pos.Y = csbi.dwCursorPosition.Y + 1;
456
457 // we decide that a line is empty if first 4 characters are spaces
458 DWORD ret;
459 char buf[4];
460 do
461 {
462 pos.Y--;
463 if ( !::ReadConsoleOutputCharacterA(m_hStderr, buf, WXSIZEOF(buf),
464 pos, &ret) )
465 {
9a83f860 466 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
784ee7d5
VZ
467 return false;
468 }
469 } while ( wxStrncmp(" ", buf, WXSIZEOF(buf)) != 0 );
470
471 // calculate line offset and length of data
472 m_dataLine = csbi.dwCursorPosition.Y - pos.Y;
473 m_dataLen = m_dataLine*csbi.dwMaximumWindowSize.X + csbi.dwCursorPosition.X;
474
475 if ( m_dataLen > 0 )
476 {
477 m_data.extend(m_dataLen);
478 if ( !::ReadConsoleOutputCharacterA(m_hStderr, m_data.data(), m_dataLen,
479 pos, &ret) )
480 {
9a83f860 481 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
784ee7d5
VZ
482 return false;
483 }
484 }
485
486 return true;
487}
488
489int wxConsoleStderr::GetCommandHistory(wxWxCharBuffer& buf) const
490{
491 // these functions are internal and may only be called by cmd.exe
9a83f860 492 static const wxChar *CMD_EXE = wxT("cmd.exe");
784ee7d5
VZ
493
494 const int len = m_pfnGetConsoleCommandHistoryLength(CMD_EXE);
495 if ( len )
496 {
497 buf.extend(len);
1033a7cc
VZ
498
499 int len2 = m_pfnGetConsoleCommandHistory(buf.data(), len, CMD_EXE);
500
501#if !wxUSE_UNICODE
502 // there seems to be a bug in the GetConsoleCommandHistoryA(), it
503 // returns the length of Unicode string and not ANSI one
504 len2 /= 2;
505#endif // !wxUSE_UNICODE
506
575cabba
VZ
507 if ( len2 != len )
508 {
9a83f860 509 wxFAIL_MSG( wxT("failed getting history?") );
575cabba 510 }
784ee7d5
VZ
511 }
512
513 return len;
514}
515
516bool wxConsoleStderr::IsHistoryUnchanged() const
517{
9a83f860 518 wxASSERT_MSG( m_ok == 1, wxT("shouldn't be called if not initialized") );
784ee7d5
VZ
519
520 // get (possibly changed) command history
521 wxWxCharBuffer history;
522 const int historyLen = GetCommandHistory(history);
523
524 // and compare it with the original one
525 return historyLen == m_historyLen && history &&
526 memcmp(m_history, history, historyLen) == 0;
527}
528
529bool wxConsoleStderr::Write(const wxString& text)
530{
531 wxASSERT_MSG( m_hStderr != INVALID_HANDLE_VALUE,
9a83f860 532 wxT("should only be called if Init() returned true") );
784ee7d5
VZ
533
534 // get current position
535 CONSOLE_SCREEN_BUFFER_INFO csbi;
536 if ( !::GetConsoleScreenBufferInfo(m_hStderr, &csbi) )
537 {
9a83f860 538 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
784ee7d5
VZ
539 return false;
540 }
541
542 // and calculate new position (where is empty line)
543 csbi.dwCursorPosition.X = 0;
544 csbi.dwCursorPosition.Y -= m_dataLine;
545
546 if ( !::SetConsoleCursorPosition(m_hStderr, csbi.dwCursorPosition) )
547 {
9a83f860 548 wxLogLastError(wxT("SetConsoleCursorPosition"));
784ee7d5
VZ
549 return false;
550 }
551
552 DWORD ret;
9a83f860 553 if ( !::FillConsoleOutputCharacter(m_hStderr, wxT(' '), m_dataLen,
784ee7d5
VZ
554 csbi.dwCursorPosition, &ret) )
555 {
9a83f860 556 wxLogLastError(wxT("FillConsoleOutputCharacter"));
784ee7d5
VZ
557 return false;
558 }
559
560 if ( !::WriteConsole(m_hStderr, text.wx_str(), text.length(), &ret, NULL) )
561 {
9a83f860 562 wxLogLastError(wxT("WriteConsole"));
784ee7d5
VZ
563 return false;
564 }
565
566 WriteConsoleA(m_hStderr, m_data, m_dataLen, &ret, 0);
567
568 return true;
569}
570
571wxConsoleStderr s_consoleStderr;
572
573} // anonymous namespace
574
575bool wxGUIAppTraits::CanUseStderr()
576{
577 return s_consoleStderr.IsOkToUse();
578}
579
580bool wxGUIAppTraits::WriteToStderr(const wxString& text)
581{
582 return s_consoleStderr.IsOkToUse() && s_consoleStderr.Write(text);
583}
584
14ca3a3b
VZ
585#else // !wxUSE_DYNLIB_CLASS
586
587bool wxGUIAppTraits::CanUseStderr()
588{
589 return false;
590}
591
592bool wxGUIAppTraits::WriteToStderr(const wxString& WXUNUSED(text))
593{
594 return false;
595}
596
597#endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
598
784ee7d5
VZ
599#endif // !__WXWINCE__
600
e2478fde
VZ
601// ===========================================================================
602// wxApp implementation
e5c0b16a 603// ===========================================================================
589f0e3e 604
94826170
VZ
605int wxApp::m_nCmdShow = SW_SHOWNORMAL;
606
e5c0b16a 607// ---------------------------------------------------------------------------
e2478fde 608// wxWin macros
e5c0b16a
VZ
609// ---------------------------------------------------------------------------
610
f6bcfd97 611IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
e5c0b16a 612
f6bcfd97
BP
613BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
614 EVT_IDLE(wxApp::OnIdle)
615 EVT_END_SESSION(wxApp::OnEndSession)
616 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
617END_EVENT_TABLE()
e5c0b16a 618
94826170
VZ
619// class to ensure that wxAppBase::CleanUp() is called if our Initialize()
620// fails
621class wxCallBaseCleanup
622{
623public:
624 wxCallBaseCleanup(wxApp *app) : m_app(app) { }
625 ~wxCallBaseCleanup() { if ( m_app ) m_app->wxAppBase::CleanUp(); }
626
627 void Dismiss() { m_app = NULL; }
628
629private:
630 wxApp *m_app;
631};
632
e5c0b16a 633//// Initialize
05e2b077 634bool wxApp::Initialize(int& argc, wxChar **argv)
2bda0e17 635{
94826170
VZ
636 if ( !wxAppBase::Initialize(argc, argv) )
637 return false;
638
639 // ensure that base cleanup is done if we return too early
640 wxCallBaseCleanup callBaseCleanup(this);
641
a71d815b 642#if !defined(__WXMICROWIN__)
a5e0e655 643 InitCommonControls();
a71d815b 644#endif // !defined(__WXMICROWIN__)
2bda0e17 645
afafd942
JS
646#if defined(__SMARTPHONE__) || defined(__POCKETPC__)
647 SHInitExtraControls();
648#endif
649
d5ea3919
JS
650#ifndef __WXWINCE__
651 // Don't show a message box if a function such as SHGetFileInfo
652 // fails to find a device.
653 SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
654#endif
ad9835c9 655
360ae33f 656 wxOleInitialize();
2bda0e17 657
4676948b 658#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
77c46f00 659 wxSetKeyboardHook(true);
04ef50df 660#endif
2bda0e17 661
94826170
VZ
662 callBaseCleanup.Dismiss();
663
664 return true;
2bda0e17
KB
665}
666
42e69d6b 667// ---------------------------------------------------------------------------
d9698bd4 668// Win32 window class registration
42e69d6b 669// ---------------------------------------------------------------------------
589f0e3e 670
d9698bd4
VZ
671/* static */
672const wxChar *wxApp::GetRegisteredClassName(const wxChar *name,
673 int bgBrushCol,
674 int extraStyles)
bcbb1359 675{
d9698bd4
VZ
676 const size_t count = gs_regClassesInfo.size();
677 for ( size_t n = 0; n < count; n++ )
bcbb1359 678 {
563cf28f 679 if ( gs_regClassesInfo[n].regname == name )
64accea5 680 return gs_regClassesInfo[n].regname.c_str();
bcbb1359 681 }
bcbb1359 682
d9698bd4 683 // we need to register this class
42e69d6b 684 WNDCLASS wndclass;
03baf031 685 wxZeroMemory(wndclass);
e5c0b16a 686
e5c0b16a 687 wndclass.lpfnWndProc = (WNDPROC)wxWndProc;
5431e4a6 688 wndclass.hInstance = wxGetInstance();
d9698bd4
VZ
689 wndclass.hCursor = ::LoadCursor(NULL, IDC_ARROW);
690 wndclass.hbrBackground = (HBRUSH)wxUIntToPtr(bgBrushCol + 1);
691 wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | extraStyles;
692
693
563cf28f 694 ClassRegInfo regClass(name);
d9698bd4
VZ
695 wndclass.lpszClassName = regClass.regname.wx_str();
696 if ( !::RegisterClass(&wndclass) )
9787a4b6 697 {
d9698bd4
VZ
698 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
699 regClass.regname));
700 return NULL;
701 }
9787a4b6 702
d9698bd4
VZ
703 wndclass.style &= ~(CS_HREDRAW | CS_VREDRAW);
704 wndclass.lpszClassName = regClass.regnameNR.wx_str();
705 if ( !::RegisterClass(&wndclass) )
706 {
707 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
708 regClass.regname));
5431e4a6 709 ::UnregisterClass(regClass.regname.c_str(), wxGetInstance());
d9698bd4 710 return NULL;
9787a4b6
VZ
711 }
712
d9698bd4 713 gs_regClassesInfo.push_back(regClass);
9787a4b6 714
d9698bd4
VZ
715 // take care to return the pointer which will remain valid after the
716 // function returns (it could be invalidated later if new elements are
717 // added to the vector and it's reallocated but this shouldn't matter as
718 // this pointer should be used right now, not stored)
719 return gs_regClassesInfo.back().regname.wx_str();
bcbb1359 720}
9787a4b6 721
d9698bd4 722bool wxApp::IsRegisteredClassName(const wxString& name)
bcbb1359 723{
d9698bd4
VZ
724 const size_t count = gs_regClassesInfo.size();
725 for ( size_t n = 0; n < count; n++ )
726 {
727 if ( gs_regClassesInfo[n].regname == name ||
728 gs_regClassesInfo[n].regnameNR == name )
729 return true;
730 }
9787a4b6 731
d9698bd4
VZ
732 return false;
733}
9787a4b6 734
d9698bd4
VZ
735void wxApp::UnregisterWindowClasses()
736{
737 const size_t count = gs_regClassesInfo.size();
738 for ( size_t n = 0; n < count; n++ )
739 {
740 const ClassRegInfo& regClass = gs_regClassesInfo[n];
5431e4a6 741 if ( !::UnregisterClass(regClass.regname.c_str(), wxGetInstance()) )
d9698bd4
VZ
742 {
743 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
744 regClass.regname));
745 }
9787a4b6 746
5431e4a6 747 if ( !::UnregisterClass(regClass.regnameNR.c_str(), wxGetInstance()) )
d9698bd4
VZ
748 {
749 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
750 regClass.regnameNR));
751 }
752 }
03baf031 753
d9698bd4 754 gs_regClassesInfo.clear();
9787a4b6
VZ
755}
756
bb6290e3 757void wxApp::CleanUp()
2bda0e17 758{
dca0f651
VZ
759 // all objects pending for deletion must be deleted first, otherwise
760 // UnregisterWindowClasses() call wouldn't succeed (because windows
761 // using the classes being unregistered still exist), so call the base
762 // class method first and only then do our clean up
7a9dfa3c
VZ
763 wxAppBase::CleanUp();
764
4676948b 765#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
77c46f00 766 wxSetKeyboardHook(false);
04ef50df 767#endif
2bda0e17 768
360ae33f 769 wxOleUninitialize();
2bda0e17 770
9787a4b6
VZ
771 // for an EXE the classes are unregistered when it terminates but DLL may
772 // be loaded several times (load/unload/load) into the same process in
773 // which case the registration will fail after the first time if we don't
774 // unregister the classes now
775 UnregisterWindowClasses();
2bda0e17
KB
776}
777
94826170
VZ
778// ----------------------------------------------------------------------------
779// wxApp ctor/dtor
780// ----------------------------------------------------------------------------
589f0e3e 781
bb6290e3 782wxApp::wxApp()
2bda0e17 783{
e5c0b16a 784 m_printMode = wxPRINT_WINDOWS;
2bda0e17
KB
785}
786
589f0e3e
JS
787wxApp::~wxApp()
788{
589f0e3e
JS
789}
790
6046e57a
VZ
791// ----------------------------------------------------------------------------
792// wxApp idle handling
793// ----------------------------------------------------------------------------
794
cb3c7fdd 795void wxApp::OnIdle(wxIdleEvent& WXUNUSED(event))
2bda0e17 796{
aef94d68
JS
797#if wxUSE_DC_CACHEING
798 // automated DC cache management: clear the cached DCs and bitmap
799 // if it's likely that the app has finished with them, that is, we
800 // get an idle event and we're not dragging anything.
4624defa 801 if (!::GetKeyState(MK_LBUTTON) && !::GetKeyState(MK_MBUTTON) && !::GetKeyState(MK_RBUTTON))
888dde65 802 wxMSWDCImpl::ClearCache();
aef94d68 803#endif // wxUSE_DC_CACHEING
2bda0e17
KB
804}
805
e2478fde
VZ
806void wxApp::WakeUpIdle()
807{
808 // Send the top window a dummy message so idle handler processing will
809 // start up again. Doing it this way ensures that the idle handler
810 // wakes up in the right thread (see also wxWakeUpMainThread() which does
811 // the same for the main app thread only)
1a18f241 812 wxWindow * const topWindow = wxTheApp->GetTopWindow();
e2478fde
VZ
813 if ( topWindow )
814 {
1a18f241
VZ
815 HWND hwndTop = GetHwndOf(topWindow);
816
817 // Do not post WM_NULL if there's already a pending WM_NULL to avoid
818 // overflowing the message queue.
819 //
820 // Notice that due to a limitation of PeekMessage() API (which handles
821 // 0,0 range specially), we have to check the range from 0-1 instead.
822 // This still makes it possible to overflow the queue with WM_NULLs by
823 // interspersing the calles to WakeUpIdle() with windows creation but
824 // it should be rather hard to do it accidentally.
825 MSG msg;
826 if ( !::PeekMessage(&msg, hwndTop, 0, 1, PM_NOREMOVE) ||
827 ::PeekMessage(&msg, hwndTop, 1, 1, PM_NOREMOVE) )
e2478fde 828 {
1a18f241
VZ
829 if ( !::PostMessage(hwndTop, WM_NULL, 0, 0) )
830 {
831 // should never happen
832 wxLogLastError(wxT("PostMessage(WM_NULL)"));
833 }
e2478fde
VZ
834 }
835 }
4a00d37c
JS
836#if wxUSE_THREADS
837 else
838 wxWakeUpMainThread();
839#endif // wxUSE_THREADS
e2478fde
VZ
840}
841
6046e57a
VZ
842// ----------------------------------------------------------------------------
843// other wxApp event hanlders
844// ----------------------------------------------------------------------------
845
57c208c5 846void wxApp::OnEndSession(wxCloseEvent& WXUNUSED(event))
387a3b02 847{
9fb99466 848 // Windows will terminate the process soon after we return from
8c6dcbd0
VZ
849 // WM_ENDSESSION handler or when we delete our last window, so make sure we
850 // at least execute our cleanup code before
851
852 // prevent the window from being destroyed when the corresponding wxTLW is
853 // destroyed: this will result in a leak of a HWND, of course, but who
854 // cares when the process is being killed anyhow
855 if ( !wxTopLevelWindows.empty() )
856 wxTopLevelWindows[0]->SetHWND(0);
857
9fb99466
VZ
858 const int rc = OnExit();
859
860 wxEntryCleanup();
861
862 // calling exit() instead of ExitProcess() or not doing anything at all and
863 // being killed by Windows has the advantage of executing the dtors of
864 // global objects
865 exit(rc);
387a3b02
JS
866}
867
868// Default behaviour: close the application with prompts. The
869// user can veto the close, and therefore the end session.
870void wxApp::OnQueryEndSession(wxCloseEvent& event)
871{
872 if (GetTopWindow())
873 {
874 if (!GetTopWindow()->Close(!event.CanVeto()))
77c46f00 875 event.Veto(true);
387a3b02
JS
876 }
877}
878
6046e57a 879// ----------------------------------------------------------------------------
5e9b921d 880// system DLL versions
6046e57a
VZ
881// ----------------------------------------------------------------------------
882
6b4296f7
VZ
883// these functions have trivial inline implementations for CE
884#ifndef __WXWINCE__
885
5e9b921d
VZ
886#if wxUSE_DYNLIB_CLASS
887
888namespace
889{
890
891// helper function: retrieve the DLL version by using DllGetVersion(), returns
892// 0 if the DLL doesn't export such function
893int CallDllGetVersion(wxDynamicLibrary& dll)
894{
895 // now check if the function is available during run-time
896 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC, DllGetVersion, dll );
897 if ( !pfnDllGetVersion )
898 return 0;
899
900 DLLVERSIONINFO dvi;
901 dvi.cbSize = sizeof(dvi);
902
903 HRESULT hr = (*pfnDllGetVersion)(&dvi);
904 if ( FAILED(hr) )
905 {
9a83f860 906 wxLogApiError(wxT("DllGetVersion"), hr);
5e9b921d
VZ
907
908 return 0;
909 }
910
911 return 100*dvi.dwMajorVersion + dvi.dwMinorVersion;
912}
913
914} // anonymous namespace
915
6d167489
VZ
916/* static */
917int wxApp::GetComCtl32Version()
918{
6d167489 919 // cache the result
9fc6c21c
VZ
920 //
921 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
922 // but as its value should be the same both times it doesn't matter
bdc72a22
VZ
923 static int s_verComCtl32 = -1;
924
6d167489
VZ
925 if ( s_verComCtl32 == -1 )
926 {
9fc6c21c
VZ
927 // we're prepared to handle the errors
928 wxLogNull noLog;
ad9835c9 929
e2d4ce7d
VZ
930 // we don't want to load comctl32.dll, it should be already loaded but,
931 // depending on the OS version and the presence of the manifest, it can
932 // be either v5 or v6 and instead of trying to guess it just get the
933 // handle of the already loaded version
9a83f860 934 wxLoadedDLL dllComCtl32(wxT("comctl32.dll"));
5e9b921d
VZ
935 if ( !dllComCtl32.IsLoaded() )
936 {
937 s_verComCtl32 = 0;
938 return 0;
939 }
6d167489 940
5e9b921d
VZ
941 // try DllGetVersion() for recent DLLs
942 s_verComCtl32 = CallDllGetVersion(dllComCtl32);
943
944 // if DllGetVersion() is unavailable either during compile or
945 // run-time, try to guess the version otherwise
946 if ( !s_verComCtl32 )
bb6290e3 947 {
5e9b921d 948 // InitCommonControlsEx is unique to 4.70 and later
9a83f860 949 void *pfn = dllComCtl32.GetSymbol(wxT("InitCommonControlsEx"));
5e9b921d 950 if ( !pfn )
9fc6c21c 951 {
5e9b921d
VZ
952 // not found, must be 4.00
953 s_verComCtl32 = 400;
9fc6c21c 954 }
5e9b921d 955 else // 4.70+
9fc6c21c 956 {
5e9b921d
VZ
957 // many symbols appeared in comctl32 4.71, could use any of
958 // them except may be DllInstall()
9a83f860 959 pfn = dllComCtl32.GetSymbol(wxT("InitializeFlatSB"));
9fc6c21c
VZ
960 if ( !pfn )
961 {
5e9b921d
VZ
962 // not found, must be 4.70
963 s_verComCtl32 = 470;
6d167489 964 }
5e9b921d 965 else
bdc72a22 966 {
5e9b921d
VZ
967 // found, must be 4.71 or later
968 s_verComCtl32 = 471;
6d167489 969 }
9fc6c21c 970 }
ef094fa0 971 }
bb6290e3 972 }
6d167489
VZ
973
974 return s_verComCtl32;
bb6290e3
JS
975}
976
5e9b921d
VZ
977/* static */
978int wxApp::GetShell32Version()
979{
980 static int s_verShell32 = -1;
981 if ( s_verShell32 == -1 )
982 {
983 // we're prepared to handle the errors
984 wxLogNull noLog;
985
9a83f860 986 wxDynamicLibrary dllShell32(wxT("shell32.dll"), wxDL_VERBATIM);
5e9b921d
VZ
987 if ( dllShell32.IsLoaded() )
988 {
989 s_verShell32 = CallDllGetVersion(dllShell32);
990
991 if ( !s_verShell32 )
992 {
993 // there doesn't seem to be any way to distinguish between 4.00
994 // and 4.70 (starting from 4.71 we have DllGetVersion()) so
995 // just assume it is 4.0
996 s_verShell32 = 400;
997 }
998 }
999 else // failed load the DLL?
1000 {
1001 s_verShell32 = 0;
1002 }
1003 }
1004
1005 return s_verShell32;
1006}
1007
1008#else // !wxUSE_DYNLIB_CLASS
1009
1010/* static */
1011int wxApp::GetComCtl32Version()
1012{
1013 return 0;
1014}
1015
1016/* static */
1017int wxApp::GetShell32Version()
1018{
1019 return 0;
1020}
1021
1022#endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
1023
6b4296f7
VZ
1024#endif // !__WXWINCE__
1025
6046e57a
VZ
1026#if wxUSE_EXCEPTIONS
1027
1028// ----------------------------------------------------------------------------
1029// exception handling
1030// ----------------------------------------------------------------------------
1031
1032bool wxApp::OnExceptionInMainLoop()
1033{
1034 // ask the user about what to do: use the Win32 API function here as it
77ffb593 1035 // could be dangerous to use any wxWidgets code in this state
6046e57a
VZ
1036 switch (
1037 ::MessageBox
1038 (
1039 NULL,
9a83f860 1040 wxT("An unhandled exception occurred. Press \"Abort\" to \
6046e57a
VZ
1041terminate the program,\r\n\
1042\"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
9a83f860 1043 wxT("Unhandled exception"),
6046e57a 1044 MB_ABORTRETRYIGNORE |
a71d815b 1045 MB_ICONERROR|
6046e57a
VZ
1046 MB_TASKMODAL
1047 )
1048 )
1049 {
1050 case IDABORT:
1051 throw;
1052
1053 default:
9a83f860 1054 wxFAIL_MSG( wxT("unexpected MessageBox() return code") );
6046e57a
VZ
1055 // fall through
1056
1057 case IDRETRY:
1058 return false;
1059
1060 case IDIGNORE:
1061 return true;
1062 }
1063}
1064
1065#endif // wxUSE_EXCEPTIONS