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
);
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
124 ClassRegInfo(const wxChar
*name
)
126 regnameNR(regname
+ wxApp::GetNoRedrawClassSuffix())
130 // the name of the registered class with and without CS_[HV]REDRAW styles
138 wxVector
<ClassRegInfo
> gs_regClassesInfo
;
140 } // anonymous namespace
142 // ----------------------------------------------------------------------------
144 // ----------------------------------------------------------------------------
146 LRESULT WXDLLEXPORT APIENTRY
wxWndProc(HWND
, UINT
, WPARAM
, LPARAM
);
148 // ===========================================================================
149 // wxGUIAppTraits implementation
150 // ===========================================================================
152 // private class which we use to pass parameters from BeforeChildWaitLoop() to
153 // AfterChildWaitLoop()
154 struct ChildWaitLoopData
156 ChildWaitLoopData(wxWindowDisabler
*wd_
, wxWindow
*winActive_
)
159 winActive
= winActive_
;
162 wxWindowDisabler
*wd
;
166 void *wxGUIAppTraits::BeforeChildWaitLoop()
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
186 // first disable all existing windows
187 wxWindowDisabler
*wd
= new wxWindowDisabler
;
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
193 wxTheApp
->GetTopWindow(),
196 wxPoint(32600, 32600),
198 wxDEFAULT_FRAME_STYLE
| wxFRAME_NO_TASKBAR
202 return new ChildWaitLoopData(wd
, winActive
);
205 void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig
)
209 ChildWaitLoopData
* const data
= (ChildWaitLoopData
*)dataOrig
;
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();
218 // 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
, int flags
)
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.
244 if ( flags
== wxTHREAD_WAIT_BLOCK
||
246 !wxEventLoop::GetActive() )
248 // Simple blocking wait.
249 return DoSimpleWaitForThread(hThread
);
252 return ::MsgWaitForMultipleObjects
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
258 QS_ALLINPUT
| // return as soon as there are any events
262 #endif // wxUSE_THREADS
264 wxPortId
wxGUIAppTraits::GetToolkitVersion(int *majVer
, int *minVer
) const
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
) )
275 *majVer
= info
.dwMajorVersion
;
277 *minVer
= info
.dwMinorVersion
;
280 #if defined(__WXHANDHELD__) || defined(__WXWINCE__)
289 wxTimerImpl
*wxGUIAppTraits::CreateTimerImpl(wxTimer
*timer
)
291 return new wxMSWTimerImpl(timer
);
294 #endif // wxUSE_TIMER
296 wxEventLoopBase
* wxGUIAppTraits::CreateEventLoop()
298 return new wxEventLoop
;
301 // ---------------------------------------------------------------------------
302 // Stuff for using console from the GUI applications
303 // ---------------------------------------------------------------------------
307 #if wxUSE_DYNLIB_CLASS
309 #include <wx/dynlib.h>
315 Helper class to manipulate console from a GUI app.
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
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.
327 class wxConsoleStderr
330 // default ctor does nothing, call Init() before using this class
333 m_hStderr
= INVALID_HANDLE_VALUE
;
343 if ( m_hStderr
!= INVALID_HANDLE_VALUE
)
345 if ( !::FreeConsole() )
347 wxLogLastError(wxT("FreeConsole"));
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
358 wxConsoleStderr
* const self
= const_cast<wxConsoleStderr
*>(this);
359 self
->m_ok
= self
->DoInit();
361 // no need to call IsHistoryUnchanged() as we just initialized
366 return m_ok
&& IsHistoryUnchanged();
370 // output the provided text on the console, return true if ok
371 bool Write(const wxString
& text
);
374 // called by Init() once only to do the real initialization
377 // retrieve the command line history into the provided buffer and return
379 int GetCommandHistory(wxWxCharBuffer
& buf
) const;
381 // check if the console history has changed
382 bool IsHistoryUnchanged() const;
384 int m_ok
; // initially -1, set to true or false by Init()
386 wxDynamicLibrary m_dllKernel32
;
388 HANDLE m_hStderr
; // console handle, if it's valid we must call
389 // FreeConsole() (even if m_ok != 1)
391 wxWxCharBuffer m_history
; // command history on startup
392 int m_historyLen
; // length command history buffer
394 wxCharBuffer m_data
; // data between empty line and cursor position
395 int m_dataLen
; // length data buffer
396 int m_dataLine
; // line offset
398 typedef DWORD (WINAPI
*GetConsoleCommandHistory_t
)(LPTSTR sCommands
,
401 typedef DWORD (WINAPI
*GetConsoleCommandHistoryLength_t
)(LPCTSTR sExeName
);
403 GetConsoleCommandHistory_t m_pfnGetConsoleCommandHistory
;
404 GetConsoleCommandHistoryLength_t m_pfnGetConsoleCommandHistoryLength
;
406 wxDECLARE_NO_COPY_CLASS(wxConsoleStderr
);
409 bool wxConsoleStderr::DoInit()
411 HANDLE hStderr
= ::GetStdHandle(STD_ERROR_HANDLE
);
413 if ( hStderr
== INVALID_HANDLE_VALUE
|| !hStderr
)
416 if ( !m_dllKernel32
.Load(wxT("kernel32.dll")) )
419 typedef BOOL (WINAPI
*AttachConsole_t
)(DWORD dwProcessId
);
420 AttachConsole_t
wxDL_INIT_FUNC(pfn
, AttachConsole
, m_dllKernel32
);
422 if ( !pfnAttachConsole
|| !pfnAttachConsole(ATTACH_PARENT_PROCESS
) )
425 // console attached, set m_hStderr now to ensure that we free it in the
429 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistory
, m_dllKernel32
);
430 if ( !m_pfnGetConsoleCommandHistory
)
433 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistoryLength
, m_dllKernel32
);
434 if ( !m_pfnGetConsoleCommandHistoryLength
)
437 // remember the current command history to be able to compare with it later
438 // in IsHistoryUnchanged()
439 m_historyLen
= GetCommandHistory(m_history
);
444 // now find the first blank line above the current position
445 CONSOLE_SCREEN_BUFFER_INFO csbi
;
447 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
449 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
455 pos
.Y
= csbi
.dwCursorPosition
.Y
+ 1;
457 // we decide that a line is empty if first 4 characters are spaces
463 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, buf
, WXSIZEOF(buf
),
466 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
469 } while ( wxStrncmp(" ", buf
, WXSIZEOF(buf
)) != 0 );
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
;
477 m_data
.extend(m_dataLen
);
478 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, m_data
.data(), m_dataLen
,
481 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
489 int wxConsoleStderr::GetCommandHistory(wxWxCharBuffer
& buf
) const
491 // these functions are internal and may only be called by cmd.exe
492 static const wxChar
*CMD_EXE
= wxT("cmd.exe");
494 const int len
= m_pfnGetConsoleCommandHistoryLength(CMD_EXE
);
499 int len2
= m_pfnGetConsoleCommandHistory(buf
.data(), len
, CMD_EXE
);
502 // there seems to be a bug in the GetConsoleCommandHistoryA(), it
503 // returns the length of Unicode string and not ANSI one
505 #endif // !wxUSE_UNICODE
509 wxFAIL_MSG( wxT("failed getting history?") );
516 bool wxConsoleStderr::IsHistoryUnchanged() const
518 wxASSERT_MSG( m_ok
== 1, wxT("shouldn't be called if not initialized") );
520 // get (possibly changed) command history
521 wxWxCharBuffer history
;
522 const int historyLen
= GetCommandHistory(history
);
524 // and compare it with the original one
525 return historyLen
== m_historyLen
&& history
&&
526 memcmp(m_history
, history
, historyLen
) == 0;
529 bool wxConsoleStderr::Write(const wxString
& text
)
531 wxASSERT_MSG( m_hStderr
!= INVALID_HANDLE_VALUE
,
532 wxT("should only be called if Init() returned true") );
534 // get current position
535 CONSOLE_SCREEN_BUFFER_INFO csbi
;
536 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
538 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
542 // and calculate new position (where is empty line)
543 csbi
.dwCursorPosition
.X
= 0;
544 csbi
.dwCursorPosition
.Y
-= m_dataLine
;
546 if ( !::SetConsoleCursorPosition(m_hStderr
, csbi
.dwCursorPosition
) )
548 wxLogLastError(wxT("SetConsoleCursorPosition"));
553 if ( !::FillConsoleOutputCharacter(m_hStderr
, wxT(' '), m_dataLen
,
554 csbi
.dwCursorPosition
, &ret
) )
556 wxLogLastError(wxT("FillConsoleOutputCharacter"));
560 if ( !::WriteConsole(m_hStderr
, text
.t_str(), text
.length(), &ret
, NULL
) )
562 wxLogLastError(wxT("WriteConsole"));
566 WriteConsoleA(m_hStderr
, m_data
, m_dataLen
, &ret
, 0);
571 wxConsoleStderr s_consoleStderr
;
573 } // anonymous namespace
575 bool wxGUIAppTraits::CanUseStderr()
577 return s_consoleStderr
.IsOkToUse();
580 bool wxGUIAppTraits::WriteToStderr(const wxString
& text
)
582 return s_consoleStderr
.IsOkToUse() && s_consoleStderr
.Write(text
);
585 #else // !wxUSE_DYNLIB_CLASS
587 bool wxGUIAppTraits::CanUseStderr()
592 bool wxGUIAppTraits::WriteToStderr(const wxString
& WXUNUSED(text
))
597 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
599 #endif // !__WXWINCE__
601 // ===========================================================================
602 // wxApp implementation
603 // ===========================================================================
605 int wxApp::m_nCmdShow
= SW_SHOWNORMAL
;
607 // ---------------------------------------------------------------------------
609 // ---------------------------------------------------------------------------
611 IMPLEMENT_DYNAMIC_CLASS(wxApp
, wxEvtHandler
)
613 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
614 EVT_IDLE(wxApp::OnIdle
)
615 EVT_END_SESSION(wxApp::OnEndSession
)
616 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession
)
619 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
621 class wxCallBaseCleanup
624 wxCallBaseCleanup(wxApp
*app
) : m_app(app
) { }
625 ~wxCallBaseCleanup() { if ( m_app
) m_app
->wxAppBase::CleanUp(); }
627 void Dismiss() { m_app
= NULL
; }
634 bool wxApp::Initialize(int& argc
, wxChar
**argv
)
636 if ( !wxAppBase::Initialize(argc
, argv
) )
639 // ensure that base cleanup is done if we return too early
640 wxCallBaseCleanup
callBaseCleanup(this);
642 #if !defined(__WXMICROWIN__)
643 InitCommonControls();
644 #endif // !defined(__WXMICROWIN__)
646 #if defined(__SMARTPHONE__) || defined(__POCKETPC__)
647 SHInitExtraControls();
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
);
658 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
659 wxSetKeyboardHook(true);
662 callBaseCleanup
.Dismiss();
667 // ---------------------------------------------------------------------------
668 // Win32 window class registration
669 // ---------------------------------------------------------------------------
672 const wxChar
*wxApp::GetRegisteredClassName(const wxChar
*name
,
676 const size_t count
= gs_regClassesInfo
.size();
677 for ( size_t n
= 0; n
< count
; n
++ )
679 if ( gs_regClassesInfo
[n
].regname
== name
)
680 return gs_regClassesInfo
[n
].regname
.c_str();
683 // we need to register this class
685 wxZeroMemory(wndclass
);
687 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
688 wndclass
.hInstance
= wxGetInstance();
689 wndclass
.hCursor
= ::LoadCursor(NULL
, IDC_ARROW
);
690 wndclass
.hbrBackground
= (HBRUSH
)wxUIntToPtr(bgBrushCol
+ 1);
691 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| extraStyles
;
694 ClassRegInfo
regClass(name
);
695 wndclass
.lpszClassName
= regClass
.regname
.t_str();
696 if ( !::RegisterClass(&wndclass
) )
698 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
703 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
704 wndclass
.lpszClassName
= regClass
.regnameNR
.t_str();
705 if ( !::RegisterClass(&wndclass
) )
707 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
709 ::UnregisterClass(regClass
.regname
.c_str(), wxGetInstance());
713 gs_regClassesInfo
.push_back(regClass
);
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
.t_str();
722 bool wxApp::IsRegisteredClassName(const wxString
& name
)
724 const size_t count
= gs_regClassesInfo
.size();
725 for ( size_t n
= 0; n
< count
; n
++ )
727 if ( gs_regClassesInfo
[n
].regname
== name
||
728 gs_regClassesInfo
[n
].regnameNR
== name
)
735 void wxApp::UnregisterWindowClasses()
737 const size_t count
= gs_regClassesInfo
.size();
738 for ( size_t n
= 0; n
< count
; n
++ )
740 const ClassRegInfo
& regClass
= gs_regClassesInfo
[n
];
741 if ( !::UnregisterClass(regClass
.regname
.c_str(), wxGetInstance()) )
743 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
747 if ( !::UnregisterClass(regClass
.regnameNR
.c_str(), wxGetInstance()) )
749 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
750 regClass
.regnameNR
));
754 gs_regClassesInfo
.clear();
757 void wxApp::CleanUp()
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
763 wxAppBase::CleanUp();
765 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
766 wxSetKeyboardHook(false);
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();
778 // ----------------------------------------------------------------------------
780 // ----------------------------------------------------------------------------
784 m_printMode
= wxPRINT_WINDOWS
;
791 // ----------------------------------------------------------------------------
792 // wxApp idle handling
793 // ----------------------------------------------------------------------------
795 void wxApp::OnIdle(wxIdleEvent
& WXUNUSED(event
))
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.
801 if (!::GetKeyState(MK_LBUTTON
) && !::GetKeyState(MK_MBUTTON
) && !::GetKeyState(MK_RBUTTON
))
802 wxMSWDCImpl::ClearCache();
803 #endif // wxUSE_DC_CACHEING
806 void wxApp::WakeUpIdle()
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)
812 wxWindow
* const topWindow
= wxTheApp
->GetTopWindow();
815 HWND hwndTop
= GetHwndOf(topWindow
);
817 // Do not post WM_NULL if there's already a pending WM_NULL to avoid
818 // overflowing the message queue.
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.
826 if ( !::PeekMessage(&msg
, hwndTop
, 0, 1, PM_NOREMOVE
) ||
827 ::PeekMessage(&msg
, hwndTop
, 1, 1, PM_NOREMOVE
) )
829 if ( !::PostMessage(hwndTop
, WM_NULL
, 0, 0) )
831 // should never happen
832 wxLogLastError(wxT("PostMessage(WM_NULL)"));
838 wxWakeUpMainThread();
839 #endif // wxUSE_THREADS
842 // ----------------------------------------------------------------------------
843 // other wxApp event hanlders
844 // ----------------------------------------------------------------------------
846 void wxApp::OnEndSession(wxCloseEvent
& WXUNUSED(event
))
848 // Windows will terminate the process soon after we return from
849 // WM_ENDSESSION handler or when we delete our last window, so make sure we
850 // at least execute our cleanup code before
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);
858 const int rc
= OnExit();
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
868 // Default behaviour: close the application with prompts. The
869 // user can veto the close, and therefore the end session.
870 void wxApp::OnQueryEndSession(wxCloseEvent
& event
)
874 if (!GetTopWindow()->Close(!event
.CanVeto()))
879 // ----------------------------------------------------------------------------
880 // system DLL versions
881 // ----------------------------------------------------------------------------
883 // these functions have trivial inline implementations for CE
886 #if wxUSE_DYNLIB_CLASS
891 // helper function: retrieve the DLL version by using DllGetVersion(), returns
892 // 0 if the DLL doesn't export such function
893 int CallDllGetVersion(wxDynamicLibrary
& dll
)
895 // now check if the function is available during run-time
896 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC
, DllGetVersion
, dll
);
897 if ( !pfnDllGetVersion
)
901 dvi
.cbSize
= sizeof(dvi
);
903 HRESULT hr
= (*pfnDllGetVersion
)(&dvi
);
906 wxLogApiError(wxT("DllGetVersion"), hr
);
911 return 100*dvi
.dwMajorVersion
+ dvi
.dwMinorVersion
;
914 } // anonymous namespace
917 int wxApp::GetComCtl32Version()
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
923 static int s_verComCtl32
= -1;
925 if ( s_verComCtl32
== -1 )
927 // we're prepared to handle the errors
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
934 wxLoadedDLL
dllComCtl32(wxT("comctl32.dll"));
935 if ( !dllComCtl32
.IsLoaded() )
941 // try DllGetVersion() for recent DLLs
942 s_verComCtl32
= CallDllGetVersion(dllComCtl32
);
944 // if DllGetVersion() is unavailable either during compile or
945 // run-time, try to guess the version otherwise
946 if ( !s_verComCtl32
)
948 // InitCommonControlsEx is unique to 4.70 and later
949 void *pfn
= dllComCtl32
.GetSymbol(wxT("InitCommonControlsEx"));
952 // not found, must be 4.00
957 // many symbols appeared in comctl32 4.71, could use any of
958 // them except may be DllInstall()
959 pfn
= dllComCtl32
.GetSymbol(wxT("InitializeFlatSB"));
962 // not found, must be 4.70
967 // found, must be 4.71 or later
974 return s_verComCtl32
;
978 int wxApp::GetShell32Version()
980 static int s_verShell32
= -1;
981 if ( s_verShell32
== -1 )
983 // we're prepared to handle the errors
986 wxDynamicLibrary
dllShell32(wxT("shell32.dll"), wxDL_VERBATIM
);
987 if ( dllShell32
.IsLoaded() )
989 s_verShell32
= CallDllGetVersion(dllShell32
);
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
999 else // failed load the DLL?
1005 return s_verShell32
;
1008 #else // !wxUSE_DYNLIB_CLASS
1011 int wxApp::GetComCtl32Version()
1017 int wxApp::GetShell32Version()
1022 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
1024 #endif // !__WXWINCE__
1026 #if wxUSE_EXCEPTIONS
1028 // ----------------------------------------------------------------------------
1029 // exception handling
1030 // ----------------------------------------------------------------------------
1032 bool wxApp::OnExceptionInMainLoop()
1034 // ask the user about what to do: use the Win32 API function here as it
1035 // could be dangerous to use any wxWidgets code in this state
1040 wxT("An unhandled exception occurred. Press \"Abort\" to \
1041 terminate the program,\r\n\
1042 \"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
1043 wxT("Unhandled exception"),
1044 MB_ABORTRETRYIGNORE
|
1054 wxFAIL_MSG( wxT("unexpected MessageBox() return code") );
1065 #endif // wxUSE_EXCEPTIONS