added wxApp::GetShell32Version() to test for shell32.dll support of balloon tooltips
[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
53 #include "wx/msw/private.h"
54 #include "wx/msw/ole/oleutils.h"
55 #include "wx/msw/private/timer.h"
56
57 #if wxUSE_TOOLTIPS
58 #include "wx/tooltip.h"
59 #endif // wxUSE_TOOLTIPS
60
61 // OLE is used for drag-and-drop, clipboard, OLE Automation..., but some
62 // compilers don't support it (missing headers, libs, ...)
63 #if defined(__GNUWIN32_OLD__) || defined(__SYMANTEC__) || defined(__SALFORDC__)
64 #undef wxUSE_OLE
65
66 #define wxUSE_OLE 0
67 #endif // broken compilers
68
69 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
70 #include <ole2.h>
71 #include <aygshell.h>
72 #endif
73
74 #if wxUSE_OLE
75 #include <ole2.h>
76 #endif
77
78 #include <string.h>
79 #include <ctype.h>
80
81 #include "wx/msw/missing.h"
82
83 // instead of including <shlwapi.h> which is not part of the core SDK and not
84 // shipped at all with other compilers, we always define the parts of it we
85 // need here ourselves
86 //
87 // NB: DLLVER_PLATFORM_WINDOWS will be defined if shlwapi.h had been somehow
88 // included already
89 #ifndef DLLVER_PLATFORM_WINDOWS
90 // hopefully we don't need to change packing as DWORDs should be already
91 // correctly aligned
92 struct DLLVERSIONINFO
93 {
94 DWORD cbSize;
95 DWORD dwMajorVersion; // Major version
96 DWORD dwMinorVersion; // Minor version
97 DWORD dwBuildNumber; // Build number
98 DWORD dwPlatformID; // DLLVER_PLATFORM_*
99 };
100
101 typedef HRESULT (CALLBACK* DLLGETVERSIONPROC)(DLLVERSIONINFO *);
102 #endif // defined(DLLVERSIONINFO)
103
104
105 // ---------------------------------------------------------------------------
106 // global variables
107 // ---------------------------------------------------------------------------
108
109 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
110 extern void wxSetKeyboardHook(bool doIt);
111 #endif
112
113 // NB: all "NoRedraw" classes must have the same names as the "normal" classes
114 // with NR suffix - wxWindow::MSWCreate() supposes this
115 #ifdef __WXWINCE__
116 WXDLLIMPEXP_CORE wxChar *wxCanvasClassName;
117 WXDLLIMPEXP_CORE wxChar *wxCanvasClassNameNR;
118 #else
119 WXDLLIMPEXP_CORE const wxChar *wxCanvasClassName = wxT("wxWindowClass");
120 WXDLLIMPEXP_CORE const wxChar *wxCanvasClassNameNR = wxT("wxWindowClassNR");
121 #endif
122 WXDLLIMPEXP_CORE const wxChar *wxMDIFrameClassName = wxT("wxMDIFrameClass");
123 WXDLLIMPEXP_CORE const wxChar *wxMDIFrameClassNameNoRedraw = wxT("wxMDIFrameClassNR");
124 WXDLLIMPEXP_CORE const wxChar *wxMDIChildFrameClassName = wxT("wxMDIChildFrameClass");
125 WXDLLIMPEXP_CORE const wxChar *wxMDIChildFrameClassNameNoRedraw = wxT("wxMDIChildFrameClassNR");
126
127 // ----------------------------------------------------------------------------
128 // private functions
129 // ----------------------------------------------------------------------------
130
131 LRESULT WXDLLEXPORT APIENTRY wxWndProc(HWND, UINT, WPARAM, LPARAM);
132
133 // ===========================================================================
134 // wxGUIAppTraits implementation
135 // ===========================================================================
136
137 // private class which we use to pass parameters from BeforeChildWaitLoop() to
138 // AfterChildWaitLoop()
139 struct ChildWaitLoopData
140 {
141 ChildWaitLoopData(wxWindowDisabler *wd_, wxWindow *winActive_)
142 {
143 wd = wd_;
144 winActive = winActive_;
145 }
146
147 wxWindowDisabler *wd;
148 wxWindow *winActive;
149 };
150
151 void *wxGUIAppTraits::BeforeChildWaitLoop()
152 {
153 /*
154 We use a dirty hack here to disable all application windows (which we
155 must do because otherwise the calls to wxYield() could lead to some very
156 unexpected reentrancies in the users code) but to avoid losing
157 focus/activation entirely when the child process terminates which would
158 happen if we simply disabled everything using wxWindowDisabler. Indeed,
159 remember that Windows will never activate a disabled window and when the
160 last childs window is closed and Windows looks for a window to activate
161 all our windows are still disabled. There is no way to enable them in
162 time because we don't know when the childs windows are going to be
163 closed, so the solution we use here is to keep one special tiny frame
164 enabled all the time. Then when the child terminates it will get
165 activated and when we close it below -- after reenabling all the other
166 windows! -- the previously active window becomes activated again and
167 everything is ok.
168 */
169 wxBeginBusyCursor();
170
171 // first disable all existing windows
172 wxWindowDisabler *wd = new wxWindowDisabler;
173
174 // then create an "invisible" frame: it has minimal size, is positioned
175 // (hopefully) outside the screen and doesn't appear on the taskbar
176 wxWindow *winActive = new wxFrame
177 (
178 wxTheApp->GetTopWindow(),
179 wxID_ANY,
180 wxEmptyString,
181 wxPoint(32600, 32600),
182 wxSize(1, 1),
183 wxDEFAULT_FRAME_STYLE | wxFRAME_NO_TASKBAR
184 );
185 winActive->Show();
186
187 return new ChildWaitLoopData(wd, winActive);
188 }
189
190 void wxGUIAppTraits::AlwaysYield()
191 {
192 wxYield();
193 }
194
195 void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig)
196 {
197 wxEndBusyCursor();
198
199 ChildWaitLoopData * const data = (ChildWaitLoopData *)dataOrig;
200
201 delete data->wd;
202
203 // finally delete the dummy frame and, as wd has been already destroyed and
204 // the other windows reenabled, the activation is going to return to the
205 // window which had had it before
206 data->winActive->Destroy();
207
208 // also delete the temporary data object itself
209 delete data;
210 }
211
212 bool wxGUIAppTraits::DoMessageFromThreadWait()
213 {
214 // we should return false only if the app should exit, i.e. only if
215 // Dispatch() determines that the main event loop should terminate
216 wxEventLoopBase * const evtLoop = wxEventLoop::GetActive();
217 if ( !evtLoop || !evtLoop->Pending() )
218 {
219 // no events means no quit event
220 return true;
221 }
222
223 return evtLoop->Dispatch();
224 }
225
226 DWORD wxGUIAppTraits::WaitForThread(WXHANDLE hThread)
227 {
228 // if we don't have a running event loop, we shouldn't wait for the
229 // messages as we never remove them from the message queue and so we enter
230 // an infinite loop as MsgWaitForMultipleObjects() keeps returning
231 // WAIT_OBJECT_0 + 1
232 if ( !wxEventLoop::GetActive() )
233 return DoSimpleWaitForThread(hThread);
234
235 return ::MsgWaitForMultipleObjects
236 (
237 1, // number of objects to wait for
238 (HANDLE *)&hThread, // the objects
239 false, // wait for any objects, not all
240 INFINITE, // no timeout
241 QS_ALLINPUT | // return as soon as there are any events
242 QS_ALLPOSTMESSAGE
243 );
244 }
245
246 wxPortId wxGUIAppTraits::GetToolkitVersion(int *majVer, int *minVer) const
247 {
248 OSVERSIONINFO info;
249 wxZeroMemory(info);
250
251 // on Windows, the toolkit version is the same of the OS version
252 // as Windows integrates the OS kernel with the GUI toolkit.
253 info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
254 if ( ::GetVersionEx(&info) )
255 {
256 if ( majVer )
257 *majVer = info.dwMajorVersion;
258 if ( minVer )
259 *minVer = info.dwMinorVersion;
260 }
261
262 #if defined(__WXHANDHELD__) || defined(__WXWINCE__)
263 return wxPORT_WINCE;
264 #else
265 return wxPORT_MSW;
266 #endif
267 }
268
269 #if wxUSE_TIMER
270
271 wxTimerImpl *wxGUIAppTraits::CreateTimerImpl(wxTimer *timer)
272 {
273 return new wxMSWTimerImpl(timer);
274 }
275
276 #endif // wxUSE_TIMER
277
278 wxEventLoopBase* wxGUIAppTraits::CreateEventLoop()
279 {
280 return new wxEventLoop;
281 }
282
283 // ===========================================================================
284 // wxApp implementation
285 // ===========================================================================
286
287 int wxApp::m_nCmdShow = SW_SHOWNORMAL;
288
289 // ---------------------------------------------------------------------------
290 // wxWin macros
291 // ---------------------------------------------------------------------------
292
293 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
294
295 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
296 EVT_IDLE(wxApp::OnIdle)
297 EVT_END_SESSION(wxApp::OnEndSession)
298 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
299 END_EVENT_TABLE()
300
301 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
302 // fails
303 class wxCallBaseCleanup
304 {
305 public:
306 wxCallBaseCleanup(wxApp *app) : m_app(app) { }
307 ~wxCallBaseCleanup() { if ( m_app ) m_app->wxAppBase::CleanUp(); }
308
309 void Dismiss() { m_app = NULL; }
310
311 private:
312 wxApp *m_app;
313 };
314
315 //// Initialize
316 bool wxApp::Initialize(int& argc, wxChar **argv)
317 {
318 if ( !wxAppBase::Initialize(argc, argv) )
319 return false;
320
321 // ensure that base cleanup is done if we return too early
322 wxCallBaseCleanup callBaseCleanup(this);
323
324 #ifdef __WXWINCE__
325 wxString tmp = GetAppName();
326 tmp += wxT("ClassName");
327 wxCanvasClassName = wxStrdup( tmp.wc_str() );
328 tmp += wxT("NR");
329 wxCanvasClassNameNR = wxStrdup( tmp.wc_str() );
330 HWND hWnd = FindWindow( wxCanvasClassNameNR, NULL );
331 if (hWnd)
332 {
333 SetForegroundWindow( (HWND)(((DWORD)hWnd)|0x01) );
334 return false;
335 }
336 #endif
337
338 #if !defined(__WXMICROWIN__)
339 InitCommonControls();
340 #endif // !defined(__WXMICROWIN__)
341
342 #if defined(__SMARTPHONE__) || defined(__POCKETPC__)
343 SHInitExtraControls();
344 #endif
345
346 #ifndef __WXWINCE__
347 // Don't show a message box if a function such as SHGetFileInfo
348 // fails to find a device.
349 SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
350 #endif
351
352 wxOleInitialize();
353
354 RegisterWindowClasses();
355
356 wxWinHandleHash = new wxWinHashTable(wxKEY_INTEGER, 100);
357
358 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
359 wxSetKeyboardHook(true);
360 #endif
361
362 callBaseCleanup.Dismiss();
363
364 return true;
365 }
366
367 // ---------------------------------------------------------------------------
368 // RegisterWindowClasses
369 // ---------------------------------------------------------------------------
370
371 // TODO we should only register classes really used by the app. For this it
372 // would be enough to just delay the class registration until an attempt
373 // to create a window of this class is made.
374 bool wxApp::RegisterWindowClasses()
375 {
376 WNDCLASS wndclass;
377 wxZeroMemory(wndclass);
378
379 // for each class we register one with CS_(V|H)REDRAW style and one
380 // without for windows created with wxNO_FULL_REDRAW_ON_REPAINT flag
381 static const long styleNormal = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
382 static const long styleNoRedraw = CS_DBLCLKS;
383
384 // the fields which are common to all classes
385 wndclass.lpfnWndProc = (WNDPROC)wxWndProc;
386 wndclass.hInstance = wxhInstance;
387 wndclass.hCursor = ::LoadCursor((HINSTANCE)NULL, IDC_ARROW);
388
389 // register the class for all normal windows
390 wndclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
391 wndclass.lpszClassName = wxCanvasClassName;
392 wndclass.style = styleNormal;
393
394 if ( !RegisterClass(&wndclass) )
395 {
396 wxLogLastError(wxT("RegisterClass(frame)"));
397 }
398
399 // "no redraw" frame
400 wndclass.lpszClassName = wxCanvasClassNameNR;
401 wndclass.style = styleNoRedraw;
402
403 if ( !RegisterClass(&wndclass) )
404 {
405 wxLogLastError(wxT("RegisterClass(no redraw frame)"));
406 }
407
408 // Register the MDI frame window class.
409 wndclass.hbrBackground = (HBRUSH)NULL; // paint MDI frame ourselves
410 wndclass.lpszClassName = wxMDIFrameClassName;
411 wndclass.style = styleNormal;
412
413 if ( !RegisterClass(&wndclass) )
414 {
415 wxLogLastError(wxT("RegisterClass(MDI parent)"));
416 }
417
418 // "no redraw" MDI frame
419 wndclass.lpszClassName = wxMDIFrameClassNameNoRedraw;
420 wndclass.style = styleNoRedraw;
421
422 if ( !RegisterClass(&wndclass) )
423 {
424 wxLogLastError(wxT("RegisterClass(no redraw MDI parent frame)"));
425 }
426
427 // Register the MDI child frame window class.
428 wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
429 wndclass.lpszClassName = wxMDIChildFrameClassName;
430 wndclass.style = styleNormal;
431
432 if ( !RegisterClass(&wndclass) )
433 {
434 wxLogLastError(wxT("RegisterClass(MDI child)"));
435 }
436
437 // "no redraw" MDI child frame
438 wndclass.lpszClassName = wxMDIChildFrameClassNameNoRedraw;
439 wndclass.style = styleNoRedraw;
440
441 if ( !RegisterClass(&wndclass) )
442 {
443 wxLogLastError(wxT("RegisterClass(no redraw MDI child)"));
444 }
445
446 return true;
447 }
448
449 // ---------------------------------------------------------------------------
450 // UnregisterWindowClasses
451 // ---------------------------------------------------------------------------
452
453 bool wxApp::UnregisterWindowClasses()
454 {
455 bool retval = true;
456
457 #ifndef __WXMICROWIN__
458 // MDI frame window class.
459 if ( !::UnregisterClass(wxMDIFrameClassName, wxhInstance) )
460 {
461 wxLogLastError(wxT("UnregisterClass(MDI parent)"));
462
463 retval = false;
464 }
465
466 // "no redraw" MDI frame
467 if ( !::UnregisterClass(wxMDIFrameClassNameNoRedraw, wxhInstance) )
468 {
469 wxLogLastError(wxT("UnregisterClass(no redraw MDI parent frame)"));
470
471 retval = false;
472 }
473
474 // MDI child frame window class.
475 if ( !::UnregisterClass(wxMDIChildFrameClassName, wxhInstance) )
476 {
477 wxLogLastError(wxT("UnregisterClass(MDI child)"));
478
479 retval = false;
480 }
481
482 // "no redraw" MDI child frame
483 if ( !::UnregisterClass(wxMDIChildFrameClassNameNoRedraw, wxhInstance) )
484 {
485 wxLogLastError(wxT("UnregisterClass(no redraw MDI child)"));
486
487 retval = false;
488 }
489
490 // canvas class name
491 if ( !::UnregisterClass(wxCanvasClassName, wxhInstance) )
492 {
493 wxLogLastError(wxT("UnregisterClass(canvas)"));
494
495 retval = false;
496 }
497
498 if ( !::UnregisterClass(wxCanvasClassNameNR, wxhInstance) )
499 {
500 wxLogLastError(wxT("UnregisterClass(no redraw canvas)"));
501
502 retval = false;
503 }
504 #endif // __WXMICROWIN__
505
506 return retval;
507 }
508
509 void wxApp::CleanUp()
510 {
511 // all objects pending for deletion must be deleted first, otherwise we
512 // would crash when they use wxWinHandleHash (and UnregisterWindowClasses()
513 // call wouldn't succeed as long as any windows still exist), so call the
514 // base class method first and only then do our clean up
515 wxAppBase::CleanUp();
516
517 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
518 wxSetKeyboardHook(false);
519 #endif
520
521 wxOleUninitialize();
522
523 // for an EXE the classes are unregistered when it terminates but DLL may
524 // be loaded several times (load/unload/load) into the same process in
525 // which case the registration will fail after the first time if we don't
526 // unregister the classes now
527 UnregisterWindowClasses();
528
529 delete wxWinHandleHash;
530 wxWinHandleHash = NULL;
531
532 #ifdef __WXWINCE__
533 free( wxCanvasClassName );
534 free( wxCanvasClassNameNR );
535 #endif
536 }
537
538 // ----------------------------------------------------------------------------
539 // wxApp ctor/dtor
540 // ----------------------------------------------------------------------------
541
542 wxApp::wxApp()
543 {
544 m_printMode = wxPRINT_WINDOWS;
545 }
546
547 wxApp::~wxApp()
548 {
549 }
550
551 // ----------------------------------------------------------------------------
552 // wxApp idle handling
553 // ----------------------------------------------------------------------------
554
555 void wxApp::OnIdle(wxIdleEvent& WXUNUSED(event))
556 {
557 #if wxUSE_DC_CACHEING
558 // automated DC cache management: clear the cached DCs and bitmap
559 // if it's likely that the app has finished with them, that is, we
560 // get an idle event and we're not dragging anything.
561 if (!::GetKeyState(MK_LBUTTON) && !::GetKeyState(MK_MBUTTON) && !::GetKeyState(MK_RBUTTON))
562 wxMSWDCImpl::ClearCache();
563 #endif // wxUSE_DC_CACHEING
564 }
565
566 void wxApp::WakeUpIdle()
567 {
568 // Send the top window a dummy message so idle handler processing will
569 // start up again. Doing it this way ensures that the idle handler
570 // wakes up in the right thread (see also wxWakeUpMainThread() which does
571 // the same for the main app thread only)
572 wxWindow *topWindow = wxTheApp->GetTopWindow();
573 if ( topWindow )
574 {
575 if ( !::PostMessage(GetHwndOf(topWindow), WM_NULL, 0, 0) )
576 {
577 // should never happen
578 wxLogLastError(wxT("PostMessage(WM_NULL)"));
579 }
580 }
581 }
582
583 // ----------------------------------------------------------------------------
584 // other wxApp event hanlders
585 // ----------------------------------------------------------------------------
586
587 void wxApp::OnEndSession(wxCloseEvent& WXUNUSED(event))
588 {
589 if (GetTopWindow())
590 GetTopWindow()->Close(true);
591 }
592
593 // Default behaviour: close the application with prompts. The
594 // user can veto the close, and therefore the end session.
595 void wxApp::OnQueryEndSession(wxCloseEvent& event)
596 {
597 if (GetTopWindow())
598 {
599 if (!GetTopWindow()->Close(!event.CanVeto()))
600 event.Veto(true);
601 }
602 }
603
604 // ----------------------------------------------------------------------------
605 // system DLL versions
606 // ----------------------------------------------------------------------------
607
608 #if wxUSE_DYNLIB_CLASS
609
610 namespace
611 {
612
613 // helper function: retrieve the DLL version by using DllGetVersion(), returns
614 // 0 if the DLL doesn't export such function
615 int CallDllGetVersion(wxDynamicLibrary& dll)
616 {
617 // now check if the function is available during run-time
618 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC, DllGetVersion, dll );
619 if ( !pfnDllGetVersion )
620 return 0;
621
622 DLLVERSIONINFO dvi;
623 dvi.cbSize = sizeof(dvi);
624
625 HRESULT hr = (*pfnDllGetVersion)(&dvi);
626 if ( FAILED(hr) )
627 {
628 wxLogApiError(_T("DllGetVersion"), hr);
629
630 return 0;
631 }
632
633 return 100*dvi.dwMajorVersion + dvi.dwMinorVersion;
634 }
635
636 } // anonymous namespace
637
638 /* static */
639 int wxApp::GetComCtl32Version()
640 {
641 // cache the result
642 //
643 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
644 // but as its value should be the same both times it doesn't matter
645 static int s_verComCtl32 = -1;
646
647 if ( s_verComCtl32 == -1 )
648 {
649 // we're prepared to handle the errors
650 wxLogNull noLog;
651
652 // the DLL should really be available
653 wxDynamicLibrary dllComCtl32(_T("comctl32.dll"), wxDL_VERBATIM);
654 if ( !dllComCtl32.IsLoaded() )
655 {
656 s_verComCtl32 = 0;
657 return 0;
658 }
659
660 // try DllGetVersion() for recent DLLs
661 s_verComCtl32 = CallDllGetVersion(dllComCtl32);
662
663 // if DllGetVersion() is unavailable either during compile or
664 // run-time, try to guess the version otherwise
665 if ( !s_verComCtl32 )
666 {
667 // InitCommonControlsEx is unique to 4.70 and later
668 void *pfn = dllComCtl32.GetSymbol(_T("InitCommonControlsEx"));
669 if ( !pfn )
670 {
671 // not found, must be 4.00
672 s_verComCtl32 = 400;
673 }
674 else // 4.70+
675 {
676 // many symbols appeared in comctl32 4.71, could use any of
677 // them except may be DllInstall()
678 pfn = dllComCtl32.GetSymbol(_T("InitializeFlatSB"));
679 if ( !pfn )
680 {
681 // not found, must be 4.70
682 s_verComCtl32 = 470;
683 }
684 else
685 {
686 // found, must be 4.71 or later
687 s_verComCtl32 = 471;
688 }
689 }
690 }
691 }
692
693 return s_verComCtl32;
694 }
695
696 /* static */
697 int wxApp::GetShell32Version()
698 {
699 static int s_verShell32 = -1;
700 if ( s_verShell32 == -1 )
701 {
702 // we're prepared to handle the errors
703 wxLogNull noLog;
704
705 wxDynamicLibrary dllShell32(_T("shell32.dll"), wxDL_VERBATIM);
706 if ( dllShell32.IsLoaded() )
707 {
708 s_verShell32 = CallDllGetVersion(dllShell32);
709
710 if ( !s_verShell32 )
711 {
712 // there doesn't seem to be any way to distinguish between 4.00
713 // and 4.70 (starting from 4.71 we have DllGetVersion()) so
714 // just assume it is 4.0
715 s_verShell32 = 400;
716 }
717 }
718 else // failed load the DLL?
719 {
720 s_verShell32 = 0;
721 }
722 }
723
724 return s_verShell32;
725 }
726
727 #else // !wxUSE_DYNLIB_CLASS
728
729 /* static */
730 int wxApp::GetComCtl32Version()
731 {
732 return 0;
733 }
734
735 /* static */
736 int wxApp::GetShell32Version()
737 {
738 return 0;
739 }
740
741 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
742
743 // ----------------------------------------------------------------------------
744 // Yield to incoming messages
745 // ----------------------------------------------------------------------------
746
747 bool wxApp::Yield(bool onlyIfNeeded)
748 {
749 // MT-FIXME
750 static bool s_inYield = false;
751
752 #if wxUSE_LOG
753 // disable log flushing from here because a call to wxYield() shouldn't
754 // normally result in message boxes popping up &c
755 wxLog::Suspend();
756 #endif // wxUSE_LOG
757
758 if ( s_inYield )
759 {
760 if ( !onlyIfNeeded )
761 {
762 wxFAIL_MSG( wxT("wxYield called recursively" ) );
763 }
764
765 return false;
766 }
767
768 s_inYield = true;
769
770 // we don't want to process WM_QUIT from here - it should be processed in
771 // the main event loop in order to stop it
772 MSG msg;
773 while ( PeekMessage(&msg, (HWND)0, 0, 0, PM_NOREMOVE) &&
774 msg.message != WM_QUIT )
775 {
776 #if wxUSE_THREADS
777 wxMutexGuiLeaveOrEnter();
778 #endif // wxUSE_THREADS
779
780 if ( !wxTheApp->Dispatch() )
781 break;
782 }
783
784 // if there are pending events, we must process them.
785 ProcessPendingEvents();
786
787 #if wxUSE_LOG
788 // let the logs be flashed again
789 wxLog::Resume();
790 #endif // wxUSE_LOG
791
792 s_inYield = false;
793
794 return true;
795 }
796
797 #if wxUSE_EXCEPTIONS
798
799 // ----------------------------------------------------------------------------
800 // exception handling
801 // ----------------------------------------------------------------------------
802
803 bool wxApp::OnExceptionInMainLoop()
804 {
805 // ask the user about what to do: use the Win32 API function here as it
806 // could be dangerous to use any wxWidgets code in this state
807 switch (
808 ::MessageBox
809 (
810 NULL,
811 _T("An unhandled exception occurred. Press \"Abort\" to \
812 terminate the program,\r\n\
813 \"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
814 _T("Unhandled exception"),
815 MB_ABORTRETRYIGNORE |
816 MB_ICONERROR|
817 MB_TASKMODAL
818 )
819 )
820 {
821 case IDABORT:
822 throw;
823
824 default:
825 wxFAIL_MSG( _T("unexpected MessageBox() return code") );
826 // fall through
827
828 case IDRETRY:
829 return false;
830
831 case IDIGNORE:
832 return true;
833 }
834 }
835
836 #endif // wxUSE_EXCEPTIONS