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