1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/app.cpp
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
23 #if defined(__BORLANDC__)
28 #include "wx/msw/wrapcctl.h"
29 #include "wx/dynarray.h"
33 #include "wx/gdicmn.h"
36 #include "wx/cursor.h"
38 #include "wx/palette.h"
40 #include "wx/dialog.h"
41 #include "wx/msgdlg.h"
45 #include "wx/module.h"
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"
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"
62 #include "wx/tooltip.h"
63 #endif // wxUSE_TOOLTIPS
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__)
71 #endif // broken compilers
73 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
85 #include "wx/msw/missing.h"
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
91 // NB: DLLVER_PLATFORM_WINDOWS will be defined if shlwapi.h had been somehow
93 #ifndef DLLVER_PLATFORM_WINDOWS
94 // hopefully we don't need to change packing as DWORDs should be already
99 DWORD dwMajorVersion
; // Major version
100 DWORD dwMinorVersion
; // Minor version
101 DWORD dwBuildNumber
; // Build number
102 DWORD dwPlatformID
; // DLLVER_PLATFORM_*
105 typedef HRESULT (CALLBACK
* DLLGETVERSIONPROC
)(DLLVERSIONINFO
*);
106 #endif // defined(DLLVERSIONINFO)
108 #ifndef ATTACH_PARENT_PROCESS
109 #define ATTACH_PARENT_PROCESS ((DWORD)-1)
112 // ---------------------------------------------------------------------------
114 // ---------------------------------------------------------------------------
116 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
117 extern void wxSetKeyboardHook(bool doIt
);
125 // the base name of the class: this is used to construct the unique name in
126 // RegisterClassWithUniqueNames()
129 // the name of the registered class with and without CS_[HV]REDRAW styles
134 wxVector
<ClassRegInfo
> gs_regClassesInfo
;
136 } // anonymous namespace
138 // ----------------------------------------------------------------------------
140 // ----------------------------------------------------------------------------
142 LRESULT WXDLLEXPORT APIENTRY
wxWndProc(HWND
, UINT
, WPARAM
, LPARAM
);
144 // ===========================================================================
145 // wxGUIAppTraits implementation
146 // ===========================================================================
148 // private class which we use to pass parameters from BeforeChildWaitLoop() to
149 // AfterChildWaitLoop()
150 struct ChildWaitLoopData
152 ChildWaitLoopData(wxWindowDisabler
*wd_
, wxWindow
*winActive_
)
155 winActive
= winActive_
;
158 wxWindowDisabler
*wd
;
162 void *wxGUIAppTraits::BeforeChildWaitLoop()
165 We use a dirty hack here to disable all application windows (which we
166 must do because otherwise the calls to wxYield() could lead to some very
167 unexpected reentrancies in the users code) but to avoid losing
168 focus/activation entirely when the child process terminates which would
169 happen if we simply disabled everything using wxWindowDisabler. Indeed,
170 remember that Windows will never activate a disabled window and when the
171 last childs window is closed and Windows looks for a window to activate
172 all our windows are still disabled. There is no way to enable them in
173 time because we don't know when the childs windows are going to be
174 closed, so the solution we use here is to keep one special tiny frame
175 enabled all the time. Then when the child terminates it will get
176 activated and when we close it below -- after reenabling all the other
177 windows! -- the previously active window becomes activated again and
182 // first disable all existing windows
183 wxWindowDisabler
*wd
= new wxWindowDisabler
;
185 // then create an "invisible" frame: it has minimal size, is positioned
186 // (hopefully) outside the screen and doesn't appear on the taskbar
187 wxWindow
*winActive
= new wxFrame
189 wxTheApp
->GetTopWindow(),
192 wxPoint(32600, 32600),
194 wxDEFAULT_FRAME_STYLE
| wxFRAME_NO_TASKBAR
198 return new ChildWaitLoopData(wd
, winActive
);
201 void wxGUIAppTraits::AlwaysYield()
206 void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig
)
210 ChildWaitLoopData
* const data
= (ChildWaitLoopData
*)dataOrig
;
214 // finally delete the dummy frame and, as wd has been already destroyed and
215 // the other windows reenabled, the activation is going to return to the
216 // window which had had it before
217 data
->winActive
->Destroy();
219 // also delete the temporary data object itself
223 bool wxGUIAppTraits::DoMessageFromThreadWait()
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
227 wxEventLoopBase
* const evtLoop
= wxEventLoop::GetActive();
228 if ( !evtLoop
|| !evtLoop
->Pending() )
230 // no events means no quit event
234 return evtLoop
->Dispatch();
237 DWORD
wxGUIAppTraits::WaitForThread(WXHANDLE hThread
)
239 // if we don't have a running event loop, we shouldn't wait for the
240 // messages as we never remove them from the message queue and so we enter
241 // an infinite loop as MsgWaitForMultipleObjects() keeps returning
243 if ( !wxEventLoop::GetActive() )
244 return DoSimpleWaitForThread(hThread
);
246 return ::MsgWaitForMultipleObjects
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
257 wxPortId
wxGUIAppTraits::GetToolkitVersion(int *majVer
, int *minVer
) const
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
) )
268 *majVer
= info
.dwMajorVersion
;
270 *minVer
= info
.dwMinorVersion
;
273 #if defined(__WXHANDHELD__) || defined(__WXWINCE__)
282 wxTimerImpl
*wxGUIAppTraits::CreateTimerImpl(wxTimer
*timer
)
284 return new wxMSWTimerImpl(timer
);
287 #endif // wxUSE_TIMER
289 wxEventLoopBase
* wxGUIAppTraits::CreateEventLoop()
291 return new wxEventLoop
;
294 // ---------------------------------------------------------------------------
295 // Stuff for using console from the GUI applications
296 // ---------------------------------------------------------------------------
300 #include <wx/dynlib.h>
306 Helper class to manipulate console from a GUI app.
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
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.
318 class wxConsoleStderr
321 // default ctor does nothing, call Init() before using this class
324 m_hStderr
= INVALID_HANDLE_VALUE
;
334 if ( m_hStderr
!= INVALID_HANDLE_VALUE
)
336 if ( !::FreeConsole() )
338 wxLogLastError(_T("FreeConsole"));
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
349 wxConsoleStderr
* const self
= const_cast<wxConsoleStderr
*>(this);
350 self
->m_ok
= self
->DoInit();
352 // no need to call IsHistoryUnchanged() as we just initialized
357 return m_ok
&& IsHistoryUnchanged();
361 // output the provided text on the console, return true if ok
362 bool Write(const wxString
& text
);
365 // called by Init() once only to do the real initialization
368 // retrieve the command line history into the provided buffer and return
370 int GetCommandHistory(wxWxCharBuffer
& buf
) const;
372 // check if the console history has changed
373 bool IsHistoryUnchanged() const;
375 int m_ok
; // initially -1, set to true or false by Init()
377 wxDynamicLibrary m_dllKernel32
;
379 HANDLE m_hStderr
; // console handle, if it's valid we must call
380 // FreeConsole() (even if m_ok != 1)
382 wxWxCharBuffer m_history
; // command history on startup
383 int m_historyLen
; // length command history buffer
385 wxCharBuffer m_data
; // data between empty line and cursor position
386 int m_dataLen
; // length data buffer
387 int m_dataLine
; // line offset
389 typedef DWORD (WINAPI
*GetConsoleCommandHistory_t
)(LPTSTR sCommands
,
392 typedef DWORD (WINAPI
*GetConsoleCommandHistoryLength_t
)(LPCTSTR sExeName
);
394 GetConsoleCommandHistory_t m_pfnGetConsoleCommandHistory
;
395 GetConsoleCommandHistoryLength_t m_pfnGetConsoleCommandHistoryLength
;
397 DECLARE_NO_COPY_CLASS(wxConsoleStderr
)
400 bool wxConsoleStderr::DoInit()
402 HANDLE hStderr
= ::GetStdHandle(STD_ERROR_HANDLE
);
404 if ( hStderr
== INVALID_HANDLE_VALUE
|| !hStderr
)
407 if ( !m_dllKernel32
.Load(_T("kernel32.dll")) )
410 typedef BOOL (WINAPI
*AttachConsole_t
)(DWORD dwProcessId
);
411 AttachConsole_t
wxDL_INIT_FUNC(pfn
, AttachConsole
, m_dllKernel32
);
413 if ( !pfnAttachConsole
|| !pfnAttachConsole(ATTACH_PARENT_PROCESS
) )
416 // console attached, set m_hStderr now to ensure that we free it in the
420 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistory
, m_dllKernel32
);
421 if ( !m_pfnGetConsoleCommandHistory
)
424 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistoryLength
, m_dllKernel32
);
425 if ( !m_pfnGetConsoleCommandHistoryLength
)
428 // remember the current command history to be able to compare with it later
429 // in IsHistoryUnchanged()
430 m_historyLen
= GetCommandHistory(m_history
);
435 // now find the first blank line above the current position
436 CONSOLE_SCREEN_BUFFER_INFO csbi
;
438 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
440 wxLogLastError(_T("GetConsoleScreenBufferInfo"));
446 pos
.Y
= csbi
.dwCursorPosition
.Y
+ 1;
448 // we decide that a line is empty if first 4 characters are spaces
454 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, buf
, WXSIZEOF(buf
),
457 wxLogLastError(_T("ReadConsoleOutputCharacterA"));
460 } while ( wxStrncmp(" ", buf
, WXSIZEOF(buf
)) != 0 );
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
;
468 m_data
.extend(m_dataLen
);
469 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, m_data
.data(), m_dataLen
,
472 wxLogLastError(_T("ReadConsoleOutputCharacterA"));
480 int wxConsoleStderr::GetCommandHistory(wxWxCharBuffer
& buf
) const
482 // these functions are internal and may only be called by cmd.exe
483 static const wxChar
*CMD_EXE
= _T("cmd.exe");
485 const int len
= m_pfnGetConsoleCommandHistoryLength(CMD_EXE
);
490 int len2
= m_pfnGetConsoleCommandHistory(buf
.data(), len
, CMD_EXE
);
493 // there seems to be a bug in the GetConsoleCommandHistoryA(), it
494 // returns the length of Unicode string and not ANSI one
496 #endif // !wxUSE_UNICODE
500 wxFAIL_MSG( _T("failed getting history?") );
507 bool wxConsoleStderr::IsHistoryUnchanged() const
509 wxASSERT_MSG( m_ok
== 1, _T("shouldn't be called if not initialized") );
511 // get (possibly changed) command history
512 wxWxCharBuffer history
;
513 const int historyLen
= GetCommandHistory(history
);
515 // and compare it with the original one
516 return historyLen
== m_historyLen
&& history
&&
517 memcmp(m_history
, history
, historyLen
) == 0;
520 bool wxConsoleStderr::Write(const wxString
& text
)
522 wxASSERT_MSG( m_hStderr
!= INVALID_HANDLE_VALUE
,
523 _T("should only be called if Init() returned true") );
525 // get current position
526 CONSOLE_SCREEN_BUFFER_INFO csbi
;
527 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
529 wxLogLastError(_T("GetConsoleScreenBufferInfo"));
533 // and calculate new position (where is empty line)
534 csbi
.dwCursorPosition
.X
= 0;
535 csbi
.dwCursorPosition
.Y
-= m_dataLine
;
537 if ( !::SetConsoleCursorPosition(m_hStderr
, csbi
.dwCursorPosition
) )
539 wxLogLastError(_T("SetConsoleCursorPosition"));
544 if ( !::FillConsoleOutputCharacter(m_hStderr
, _T(' '), m_dataLen
,
545 csbi
.dwCursorPosition
, &ret
) )
547 wxLogLastError(_T("FillConsoleOutputCharacter"));
551 if ( !::WriteConsole(m_hStderr
, text
.wx_str(), text
.length(), &ret
, NULL
) )
553 wxLogLastError(_T("WriteConsole"));
557 WriteConsoleA(m_hStderr
, m_data
, m_dataLen
, &ret
, 0);
562 wxConsoleStderr s_consoleStderr
;
564 } // anonymous namespace
566 bool wxGUIAppTraits::CanUseStderr()
568 return s_consoleStderr
.IsOkToUse();
571 bool wxGUIAppTraits::WriteToStderr(const wxString
& text
)
573 return s_consoleStderr
.IsOkToUse() && s_consoleStderr
.Write(text
);
576 #endif // !__WXWINCE__
578 // ===========================================================================
579 // wxApp implementation
580 // ===========================================================================
582 int wxApp::m_nCmdShow
= SW_SHOWNORMAL
;
584 // ---------------------------------------------------------------------------
586 // ---------------------------------------------------------------------------
588 IMPLEMENT_DYNAMIC_CLASS(wxApp
, wxEvtHandler
)
590 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
591 EVT_IDLE(wxApp::OnIdle
)
592 EVT_END_SESSION(wxApp::OnEndSession
)
593 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession
)
596 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
598 class wxCallBaseCleanup
601 wxCallBaseCleanup(wxApp
*app
) : m_app(app
) { }
602 ~wxCallBaseCleanup() { if ( m_app
) m_app
->wxAppBase::CleanUp(); }
604 void Dismiss() { m_app
= NULL
; }
611 bool wxApp::Initialize(int& argc
, wxChar
**argv
)
613 if ( !wxAppBase::Initialize(argc
, argv
) )
616 // ensure that base cleanup is done if we return too early
617 wxCallBaseCleanup
callBaseCleanup(this);
619 #if !defined(__WXMICROWIN__)
620 InitCommonControls();
621 #endif // !defined(__WXMICROWIN__)
623 #if defined(__SMARTPHONE__) || defined(__POCKETPC__)
624 SHInitExtraControls();
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
);
635 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
636 wxSetKeyboardHook(true);
639 callBaseCleanup
.Dismiss();
644 // ---------------------------------------------------------------------------
645 // Win32 window class registration
646 // ---------------------------------------------------------------------------
649 const wxChar
*wxApp::GetRegisteredClassName(const wxChar
*name
,
653 const size_t count
= gs_regClassesInfo
.size();
654 for ( size_t n
= 0; n
< count
; n
++ )
656 if ( gs_regClassesInfo
[n
].basename
== name
)
657 return gs_regClassesInfo
[n
].regname
.c_str();
660 // we need to register this class
662 wxZeroMemory(wndclass
);
664 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
665 wndclass
.hInstance
= wxhInstance
;
666 wndclass
.hCursor
= ::LoadCursor(NULL
, IDC_ARROW
);
667 wndclass
.hbrBackground
= (HBRUSH
)wxUIntToPtr(bgBrushCol
+ 1);
668 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| extraStyles
;
671 ClassRegInfo regClass
;
672 regClass
.basename
= name
;
674 // constuct a unique suffix to allow registering the class with the same
675 // base name in a main application using wxWidgets and a DLL using
676 // wxWidgets loaded into its address space: as gs_regClassesInfo variable
677 // is different in them, we're going to obtain a unique prefix by using its
679 regClass
.regname
= regClass
.basename
+
680 wxString::Format(wxT("@%p"), &gs_regClassesInfo
);
681 wndclass
.lpszClassName
= regClass
.regname
.wx_str();
682 if ( !::RegisterClass(&wndclass
) )
684 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
689 // NB: remember that code elsewhere supposes that no redraw class names
690 // use the same names as normal classes with "NR" suffix so we must put
691 // "NR" at the end instead of using more natural basename+"NR"+suffix
692 regClass
.regnameNR
= regClass
.regname
+ GetNoRedrawClassSuffix();
693 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
694 wndclass
.lpszClassName
= regClass
.regnameNR
.wx_str();
695 if ( !::RegisterClass(&wndclass
) )
697 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
699 ::UnregisterClass(regClass
.regname
.c_str(), wxhInstance
);
703 gs_regClassesInfo
.push_back(regClass
);
705 // take care to return the pointer which will remain valid after the
706 // function returns (it could be invalidated later if new elements are
707 // added to the vector and it's reallocated but this shouldn't matter as
708 // this pointer should be used right now, not stored)
709 return gs_regClassesInfo
.back().regname
.wx_str();
712 bool wxApp::IsRegisteredClassName(const wxString
& name
)
714 const size_t count
= gs_regClassesInfo
.size();
715 for ( size_t n
= 0; n
< count
; n
++ )
717 if ( gs_regClassesInfo
[n
].regname
== name
||
718 gs_regClassesInfo
[n
].regnameNR
== name
)
725 void wxApp::UnregisterWindowClasses()
727 const size_t count
= gs_regClassesInfo
.size();
728 for ( size_t n
= 0; n
< count
; n
++ )
730 const ClassRegInfo
& regClass
= gs_regClassesInfo
[n
];
731 if ( !::UnregisterClass(regClass
.regname
.c_str(), wxhInstance
) )
733 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
737 if ( !::UnregisterClass(regClass
.regnameNR
.c_str(), wxhInstance
) )
739 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
740 regClass
.regnameNR
));
744 gs_regClassesInfo
.clear();
747 void wxApp::CleanUp()
749 // all objects pending for deletion must be deleted first, otherwise
750 // UnregisterWindowClasses() call wouldn't succeed (because windows
751 // using the classes being unregistered still exist), so call the base
752 // class method first and only then do our clean up
753 wxAppBase::CleanUp();
755 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
756 wxSetKeyboardHook(false);
761 // for an EXE the classes are unregistered when it terminates but DLL may
762 // be loaded several times (load/unload/load) into the same process in
763 // which case the registration will fail after the first time if we don't
764 // unregister the classes now
765 UnregisterWindowClasses();
768 // ----------------------------------------------------------------------------
770 // ----------------------------------------------------------------------------
774 m_printMode
= wxPRINT_WINDOWS
;
781 // ----------------------------------------------------------------------------
782 // wxApp idle handling
783 // ----------------------------------------------------------------------------
785 void wxApp::OnIdle(wxIdleEvent
& WXUNUSED(event
))
787 #if wxUSE_DC_CACHEING
788 // automated DC cache management: clear the cached DCs and bitmap
789 // if it's likely that the app has finished with them, that is, we
790 // get an idle event and we're not dragging anything.
791 if (!::GetKeyState(MK_LBUTTON
) && !::GetKeyState(MK_MBUTTON
) && !::GetKeyState(MK_RBUTTON
))
792 wxMSWDCImpl::ClearCache();
793 #endif // wxUSE_DC_CACHEING
796 void wxApp::WakeUpIdle()
798 // Send the top window a dummy message so idle handler processing will
799 // start up again. Doing it this way ensures that the idle handler
800 // wakes up in the right thread (see also wxWakeUpMainThread() which does
801 // the same for the main app thread only)
802 wxWindow
* const topWindow
= wxTheApp
->GetTopWindow();
805 HWND hwndTop
= GetHwndOf(topWindow
);
807 // Do not post WM_NULL if there's already a pending WM_NULL to avoid
808 // overflowing the message queue.
810 // Notice that due to a limitation of PeekMessage() API (which handles
811 // 0,0 range specially), we have to check the range from 0-1 instead.
812 // This still makes it possible to overflow the queue with WM_NULLs by
813 // interspersing the calles to WakeUpIdle() with windows creation but
814 // it should be rather hard to do it accidentally.
816 if ( !::PeekMessage(&msg
, hwndTop
, 0, 1, PM_NOREMOVE
) ||
817 ::PeekMessage(&msg
, hwndTop
, 1, 1, PM_NOREMOVE
) )
819 if ( !::PostMessage(hwndTop
, WM_NULL
, 0, 0) )
821 // should never happen
822 wxLogLastError(wxT("PostMessage(WM_NULL)"));
828 // ----------------------------------------------------------------------------
829 // other wxApp event hanlders
830 // ----------------------------------------------------------------------------
832 void wxApp::OnEndSession(wxCloseEvent
& WXUNUSED(event
))
834 // Windows will terminate the process soon after we return from
835 // WM_ENDSESSION handler or when we delete our last window, so make sure we
836 // at least execute our cleanup code before
838 // prevent the window from being destroyed when the corresponding wxTLW is
839 // destroyed: this will result in a leak of a HWND, of course, but who
840 // cares when the process is being killed anyhow
841 if ( !wxTopLevelWindows
.empty() )
842 wxTopLevelWindows
[0]->SetHWND(0);
844 const int rc
= OnExit();
848 // calling exit() instead of ExitProcess() or not doing anything at all and
849 // being killed by Windows has the advantage of executing the dtors of
854 // Default behaviour: close the application with prompts. The
855 // user can veto the close, and therefore the end session.
856 void wxApp::OnQueryEndSession(wxCloseEvent
& event
)
860 if (!GetTopWindow()->Close(!event
.CanVeto()))
865 // ----------------------------------------------------------------------------
866 // system DLL versions
867 // ----------------------------------------------------------------------------
869 // these functions have trivial inline implementations for CE
872 #if wxUSE_DYNLIB_CLASS
877 // helper function: retrieve the DLL version by using DllGetVersion(), returns
878 // 0 if the DLL doesn't export such function
879 int CallDllGetVersion(wxDynamicLibrary
& dll
)
881 // now check if the function is available during run-time
882 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC
, DllGetVersion
, dll
);
883 if ( !pfnDllGetVersion
)
887 dvi
.cbSize
= sizeof(dvi
);
889 HRESULT hr
= (*pfnDllGetVersion
)(&dvi
);
892 wxLogApiError(_T("DllGetVersion"), hr
);
897 return 100*dvi
.dwMajorVersion
+ dvi
.dwMinorVersion
;
900 } // anonymous namespace
903 int wxApp::GetComCtl32Version()
907 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
908 // but as its value should be the same both times it doesn't matter
909 static int s_verComCtl32
= -1;
911 if ( s_verComCtl32
== -1 )
913 // we're prepared to handle the errors
916 // we don't want to load comctl32.dll, it should be already loaded but,
917 // depending on the OS version and the presence of the manifest, it can
918 // be either v5 or v6 and instead of trying to guess it just get the
919 // handle of the already loaded version
920 wxLoadedDLL
dllComCtl32(_T("comctl32.dll"));
921 if ( !dllComCtl32
.IsLoaded() )
927 // try DllGetVersion() for recent DLLs
928 s_verComCtl32
= CallDllGetVersion(dllComCtl32
);
930 // if DllGetVersion() is unavailable either during compile or
931 // run-time, try to guess the version otherwise
932 if ( !s_verComCtl32
)
934 // InitCommonControlsEx is unique to 4.70 and later
935 void *pfn
= dllComCtl32
.GetSymbol(_T("InitCommonControlsEx"));
938 // not found, must be 4.00
943 // many symbols appeared in comctl32 4.71, could use any of
944 // them except may be DllInstall()
945 pfn
= dllComCtl32
.GetSymbol(_T("InitializeFlatSB"));
948 // not found, must be 4.70
953 // found, must be 4.71 or later
960 return s_verComCtl32
;
964 int wxApp::GetShell32Version()
966 static int s_verShell32
= -1;
967 if ( s_verShell32
== -1 )
969 // we're prepared to handle the errors
972 wxDynamicLibrary
dllShell32(_T("shell32.dll"), wxDL_VERBATIM
);
973 if ( dllShell32
.IsLoaded() )
975 s_verShell32
= CallDllGetVersion(dllShell32
);
979 // there doesn't seem to be any way to distinguish between 4.00
980 // and 4.70 (starting from 4.71 we have DllGetVersion()) so
981 // just assume it is 4.0
985 else // failed load the DLL?
994 #else // !wxUSE_DYNLIB_CLASS
997 int wxApp::GetComCtl32Version()
1003 int wxApp::GetShell32Version()
1008 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
1010 #endif // !__WXWINCE__
1012 // ----------------------------------------------------------------------------
1013 // Yield to incoming messages
1014 // ----------------------------------------------------------------------------
1016 bool wxApp::Yield(bool onlyIfNeeded
)
1019 static bool s_inYield
= false;
1023 if ( !onlyIfNeeded
)
1025 wxFAIL_MSG( wxT("wxYield called recursively" ) );
1031 // set the flag and don't forget to reset it before returning
1033 wxON_BLOCK_EXIT_SET(s_inYield
, false);
1037 // disable log flushing from here because a call to wxYield() shouldn't
1038 // normally result in message boxes popping up &c
1041 // ensure the logs will be flashed again when we exit
1042 wxON_BLOCK_EXIT0(wxLog::Resume
);
1046 // we don't want to process WM_QUIT from here - it should be processed in
1047 // the main event loop in order to stop it
1048 wxEventLoopGuarantor dummyLoopIfNeeded
;
1050 while ( PeekMessage(&msg
, (HWND
)0, 0, 0, PM_NOREMOVE
) &&
1051 msg
.message
!= WM_QUIT
)
1054 wxMutexGuiLeaveOrEnter();
1055 #endif // wxUSE_THREADS
1057 if ( !wxTheApp
->Dispatch() )
1061 // if there are pending events, we must process them.
1062 ProcessPendingEvents();
1067 #if wxUSE_EXCEPTIONS
1069 // ----------------------------------------------------------------------------
1070 // exception handling
1071 // ----------------------------------------------------------------------------
1073 bool wxApp::OnExceptionInMainLoop()
1075 // ask the user about what to do: use the Win32 API function here as it
1076 // could be dangerous to use any wxWidgets code in this state
1081 _T("An unhandled exception occurred. Press \"Abort\" to \
1082 terminate the program,\r\n\
1083 \"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
1084 _T("Unhandled exception"),
1085 MB_ABORTRETRYIGNORE
|
1095 wxFAIL_MSG( _T("unexpected MessageBox() return code") );
1106 #endif // wxUSE_EXCEPTIONS