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 // the base name of the class: this is used to construct the unique name in
125 // wxApp::GetRegisteredClassName()
128 // the name of the registered class with and without CS_[HV]REDRAW styles
136 wxVector
<ClassRegInfo
> gs_regClassesInfo
;
138 } // anonymous namespace
140 // ----------------------------------------------------------------------------
142 // ----------------------------------------------------------------------------
144 LRESULT WXDLLEXPORT APIENTRY
wxWndProc(HWND
, UINT
, WPARAM
, LPARAM
);
146 // ===========================================================================
147 // wxGUIAppTraits implementation
148 // ===========================================================================
150 // private class which we use to pass parameters from BeforeChildWaitLoop() to
151 // AfterChildWaitLoop()
152 struct ChildWaitLoopData
154 ChildWaitLoopData(wxWindowDisabler
*wd_
, wxWindow
*winActive_
)
157 winActive
= winActive_
;
160 wxWindowDisabler
*wd
;
164 void *wxGUIAppTraits::BeforeChildWaitLoop()
167 We use a dirty hack here to disable all application windows (which we
168 must do because otherwise the calls to wxYield() could lead to some very
169 unexpected reentrancies in the users code) but to avoid losing
170 focus/activation entirely when the child process terminates which would
171 happen if we simply disabled everything using wxWindowDisabler. Indeed,
172 remember that Windows will never activate a disabled window and when the
173 last childs window is closed and Windows looks for a window to activate
174 all our windows are still disabled. There is no way to enable them in
175 time because we don't know when the childs windows are going to be
176 closed, so the solution we use here is to keep one special tiny frame
177 enabled all the time. Then when the child terminates it will get
178 activated and when we close it below -- after reenabling all the other
179 windows! -- the previously active window becomes activated again and
184 // first disable all existing windows
185 wxWindowDisabler
*wd
= new wxWindowDisabler
;
187 // then create an "invisible" frame: it has minimal size, is positioned
188 // (hopefully) outside the screen and doesn't appear on the taskbar
189 wxWindow
*winActive
= new wxFrame
191 wxTheApp
->GetTopWindow(),
194 wxPoint(32600, 32600),
196 wxDEFAULT_FRAME_STYLE
| wxFRAME_NO_TASKBAR
200 return new ChildWaitLoopData(wd
, winActive
);
203 void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig
)
207 ChildWaitLoopData
* const data
= (ChildWaitLoopData
*)dataOrig
;
211 // finally delete the dummy frame and, as wd has been already destroyed and
212 // the other windows reenabled, the activation is going to return to the
213 // window which had had it before
214 data
->winActive
->Destroy();
216 // also delete the temporary data object itself
220 bool wxGUIAppTraits::DoMessageFromThreadWait()
222 // we should return false only if the app should exit, i.e. only if
223 // Dispatch() determines that the main event loop should terminate
224 wxEventLoopBase
* const evtLoop
= wxEventLoop::GetActive();
225 if ( !evtLoop
|| !evtLoop
->Pending() )
227 // no events means no quit event
231 return evtLoop
->Dispatch();
234 DWORD
wxGUIAppTraits::WaitForThread(WXHANDLE hThread
)
236 // if we don't have a running event loop, we shouldn't wait for the
237 // messages as we never remove them from the message queue and so we enter
238 // an infinite loop as MsgWaitForMultipleObjects() keeps returning
240 if ( !wxEventLoop::GetActive() )
241 return DoSimpleWaitForThread(hThread
);
243 return ::MsgWaitForMultipleObjects
245 1, // number of objects to wait for
246 (HANDLE
*)&hThread
, // the objects
247 false, // wait for any objects, not all
248 INFINITE
, // no timeout
249 QS_ALLINPUT
| // return as soon as there are any events
254 wxPortId
wxGUIAppTraits::GetToolkitVersion(int *majVer
, int *minVer
) const
259 // on Windows, the toolkit version is the same of the OS version
260 // as Windows integrates the OS kernel with the GUI toolkit.
261 info
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
262 if ( ::GetVersionEx(&info
) )
265 *majVer
= info
.dwMajorVersion
;
267 *minVer
= info
.dwMinorVersion
;
270 #if defined(__WXHANDHELD__) || defined(__WXWINCE__)
279 wxTimerImpl
*wxGUIAppTraits::CreateTimerImpl(wxTimer
*timer
)
281 return new wxMSWTimerImpl(timer
);
284 #endif // wxUSE_TIMER
286 wxEventLoopBase
* wxGUIAppTraits::CreateEventLoop()
288 return new wxEventLoop
;
291 // ---------------------------------------------------------------------------
292 // Stuff for using console from the GUI applications
293 // ---------------------------------------------------------------------------
297 #include <wx/dynlib.h>
303 Helper class to manipulate console from a GUI app.
305 Notice that console output is available in the GUI app only if:
306 - AttachConsole() returns TRUE (which means it never works under pre-XP)
307 - we have a valid STD_ERROR_HANDLE
308 - command history hasn't been changed since our startup
310 To check if all these conditions are verified, you need to simple call
311 IsOkToUse(). It will check the first two conditions above the first time it
312 is called (and if this fails, the subsequent calls will return immediately)
313 and also recheck the last one every time it is called.
315 class wxConsoleStderr
318 // default ctor does nothing, call Init() before using this class
321 m_hStderr
= INVALID_HANDLE_VALUE
;
331 if ( m_hStderr
!= INVALID_HANDLE_VALUE
)
333 if ( !::FreeConsole() )
335 wxLogLastError(wxT("FreeConsole"));
340 // return true if we were successfully initialized and there had been no
341 // console activity which would interfere with our output since then
342 bool IsOkToUse() const
346 wxConsoleStderr
* const self
= const_cast<wxConsoleStderr
*>(this);
347 self
->m_ok
= self
->DoInit();
349 // no need to call IsHistoryUnchanged() as we just initialized
354 return m_ok
&& IsHistoryUnchanged();
358 // output the provided text on the console, return true if ok
359 bool Write(const wxString
& text
);
362 // called by Init() once only to do the real initialization
365 // retrieve the command line history into the provided buffer and return
367 int GetCommandHistory(wxWxCharBuffer
& buf
) const;
369 // check if the console history has changed
370 bool IsHistoryUnchanged() const;
372 int m_ok
; // initially -1, set to true or false by Init()
374 wxDynamicLibrary m_dllKernel32
;
376 HANDLE m_hStderr
; // console handle, if it's valid we must call
377 // FreeConsole() (even if m_ok != 1)
379 wxWxCharBuffer m_history
; // command history on startup
380 int m_historyLen
; // length command history buffer
382 wxCharBuffer m_data
; // data between empty line and cursor position
383 int m_dataLen
; // length data buffer
384 int m_dataLine
; // line offset
386 typedef DWORD (WINAPI
*GetConsoleCommandHistory_t
)(LPTSTR sCommands
,
389 typedef DWORD (WINAPI
*GetConsoleCommandHistoryLength_t
)(LPCTSTR sExeName
);
391 GetConsoleCommandHistory_t m_pfnGetConsoleCommandHistory
;
392 GetConsoleCommandHistoryLength_t m_pfnGetConsoleCommandHistoryLength
;
394 wxDECLARE_NO_COPY_CLASS(wxConsoleStderr
);
397 bool wxConsoleStderr::DoInit()
399 HANDLE hStderr
= ::GetStdHandle(STD_ERROR_HANDLE
);
401 if ( hStderr
== INVALID_HANDLE_VALUE
|| !hStderr
)
404 if ( !m_dllKernel32
.Load(wxT("kernel32.dll")) )
407 typedef BOOL (WINAPI
*AttachConsole_t
)(DWORD dwProcessId
);
408 AttachConsole_t
wxDL_INIT_FUNC(pfn
, AttachConsole
, m_dllKernel32
);
410 if ( !pfnAttachConsole
|| !pfnAttachConsole(ATTACH_PARENT_PROCESS
) )
413 // console attached, set m_hStderr now to ensure that we free it in the
417 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistory
, m_dllKernel32
);
418 if ( !m_pfnGetConsoleCommandHistory
)
421 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistoryLength
, m_dllKernel32
);
422 if ( !m_pfnGetConsoleCommandHistoryLength
)
425 // remember the current command history to be able to compare with it later
426 // in IsHistoryUnchanged()
427 m_historyLen
= GetCommandHistory(m_history
);
432 // now find the first blank line above the current position
433 CONSOLE_SCREEN_BUFFER_INFO csbi
;
435 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
437 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
443 pos
.Y
= csbi
.dwCursorPosition
.Y
+ 1;
445 // we decide that a line is empty if first 4 characters are spaces
451 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, buf
, WXSIZEOF(buf
),
454 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
457 } while ( wxStrncmp(" ", buf
, WXSIZEOF(buf
)) != 0 );
459 // calculate line offset and length of data
460 m_dataLine
= csbi
.dwCursorPosition
.Y
- pos
.Y
;
461 m_dataLen
= m_dataLine
*csbi
.dwMaximumWindowSize
.X
+ csbi
.dwCursorPosition
.X
;
465 m_data
.extend(m_dataLen
);
466 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, m_data
.data(), m_dataLen
,
469 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
477 int wxConsoleStderr::GetCommandHistory(wxWxCharBuffer
& buf
) const
479 // these functions are internal and may only be called by cmd.exe
480 static const wxChar
*CMD_EXE
= wxT("cmd.exe");
482 const int len
= m_pfnGetConsoleCommandHistoryLength(CMD_EXE
);
487 int len2
= m_pfnGetConsoleCommandHistory(buf
.data(), len
, CMD_EXE
);
490 // there seems to be a bug in the GetConsoleCommandHistoryA(), it
491 // returns the length of Unicode string and not ANSI one
493 #endif // !wxUSE_UNICODE
497 wxFAIL_MSG( wxT("failed getting history?") );
504 bool wxConsoleStderr::IsHistoryUnchanged() const
506 wxASSERT_MSG( m_ok
== 1, wxT("shouldn't be called if not initialized") );
508 // get (possibly changed) command history
509 wxWxCharBuffer history
;
510 const int historyLen
= GetCommandHistory(history
);
512 // and compare it with the original one
513 return historyLen
== m_historyLen
&& history
&&
514 memcmp(m_history
, history
, historyLen
) == 0;
517 bool wxConsoleStderr::Write(const wxString
& text
)
519 wxASSERT_MSG( m_hStderr
!= INVALID_HANDLE_VALUE
,
520 wxT("should only be called if Init() returned true") );
522 // get current position
523 CONSOLE_SCREEN_BUFFER_INFO csbi
;
524 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
526 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
530 // and calculate new position (where is empty line)
531 csbi
.dwCursorPosition
.X
= 0;
532 csbi
.dwCursorPosition
.Y
-= m_dataLine
;
534 if ( !::SetConsoleCursorPosition(m_hStderr
, csbi
.dwCursorPosition
) )
536 wxLogLastError(wxT("SetConsoleCursorPosition"));
541 if ( !::FillConsoleOutputCharacter(m_hStderr
, wxT(' '), m_dataLen
,
542 csbi
.dwCursorPosition
, &ret
) )
544 wxLogLastError(wxT("FillConsoleOutputCharacter"));
548 if ( !::WriteConsole(m_hStderr
, text
.wx_str(), text
.length(), &ret
, NULL
) )
550 wxLogLastError(wxT("WriteConsole"));
554 WriteConsoleA(m_hStderr
, m_data
, m_dataLen
, &ret
, 0);
559 wxConsoleStderr s_consoleStderr
;
561 } // anonymous namespace
563 bool wxGUIAppTraits::CanUseStderr()
565 return s_consoleStderr
.IsOkToUse();
568 bool wxGUIAppTraits::WriteToStderr(const wxString
& text
)
570 return s_consoleStderr
.IsOkToUse() && s_consoleStderr
.Write(text
);
573 #endif // !__WXWINCE__
575 // ===========================================================================
576 // wxApp implementation
577 // ===========================================================================
579 int wxApp::m_nCmdShow
= SW_SHOWNORMAL
;
581 // ---------------------------------------------------------------------------
583 // ---------------------------------------------------------------------------
585 IMPLEMENT_DYNAMIC_CLASS(wxApp
, wxEvtHandler
)
587 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
588 EVT_IDLE(wxApp::OnIdle
)
589 EVT_END_SESSION(wxApp::OnEndSession
)
590 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession
)
593 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
595 class wxCallBaseCleanup
598 wxCallBaseCleanup(wxApp
*app
) : m_app(app
) { }
599 ~wxCallBaseCleanup() { if ( m_app
) m_app
->wxAppBase::CleanUp(); }
601 void Dismiss() { m_app
= NULL
; }
608 bool wxApp::Initialize(int& argc
, wxChar
**argv
)
610 if ( !wxAppBase::Initialize(argc
, argv
) )
613 // ensure that base cleanup is done if we return too early
614 wxCallBaseCleanup
callBaseCleanup(this);
616 #if !defined(__WXMICROWIN__)
617 InitCommonControls();
618 #endif // !defined(__WXMICROWIN__)
620 #if defined(__SMARTPHONE__) || defined(__POCKETPC__)
621 SHInitExtraControls();
625 // Don't show a message box if a function such as SHGetFileInfo
626 // fails to find a device.
627 SetErrorMode(SEM_FAILCRITICALERRORS
|SEM_NOOPENFILEERRORBOX
);
632 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
633 wxSetKeyboardHook(true);
636 callBaseCleanup
.Dismiss();
641 // ---------------------------------------------------------------------------
642 // Win32 window class registration
643 // ---------------------------------------------------------------------------
646 const wxChar
*wxApp::GetRegisteredClassName(const wxChar
*name
,
650 const size_t count
= gs_regClassesInfo
.size();
651 for ( size_t n
= 0; n
< count
; n
++ )
653 if ( gs_regClassesInfo
[n
].basename
== name
)
654 return gs_regClassesInfo
[n
].regname
.c_str();
657 // we need to register this class
659 wxZeroMemory(wndclass
);
661 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
662 wndclass
.hInstance
= wxGetInstance();
663 wndclass
.hCursor
= ::LoadCursor(NULL
, IDC_ARROW
);
664 wndclass
.hbrBackground
= (HBRUSH
)wxUIntToPtr(bgBrushCol
+ 1);
665 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| extraStyles
;
668 ClassRegInfo regClass
;
669 regClass
.basename
= name
;
671 // constuct a unique suffix to allow registering the class with the same
672 // base name in a main application using wxWidgets and a DLL using
673 // wxWidgets loaded into its address space: as gs_regClassesInfo variable
674 // is different in them, we're going to obtain a unique prefix by using its
676 regClass
.regname
= regClass
.basename
+
677 wxString::Format(wxT("@%p"), &gs_regClassesInfo
);
678 wndclass
.lpszClassName
= regClass
.regname
.wx_str();
679 if ( !::RegisterClass(&wndclass
) )
681 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
686 // NB: remember that code elsewhere supposes that no redraw class names
687 // use the same names as normal classes with "NR" suffix so we must put
688 // "NR" at the end instead of using more natural basename+"NR"+suffix
689 regClass
.regnameNR
= regClass
.regname
+ GetNoRedrawClassSuffix();
690 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
691 wndclass
.lpszClassName
= regClass
.regnameNR
.wx_str();
692 if ( !::RegisterClass(&wndclass
) )
694 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
696 ::UnregisterClass(regClass
.regname
.c_str(), wxGetInstance());
700 gs_regClassesInfo
.push_back(regClass
);
702 // take care to return the pointer which will remain valid after the
703 // function returns (it could be invalidated later if new elements are
704 // added to the vector and it's reallocated but this shouldn't matter as
705 // this pointer should be used right now, not stored)
706 return gs_regClassesInfo
.back().regname
.wx_str();
709 bool wxApp::IsRegisteredClassName(const wxString
& name
)
711 const size_t count
= gs_regClassesInfo
.size();
712 for ( size_t n
= 0; n
< count
; n
++ )
714 if ( gs_regClassesInfo
[n
].regname
== name
||
715 gs_regClassesInfo
[n
].regnameNR
== name
)
722 void wxApp::UnregisterWindowClasses()
724 const size_t count
= gs_regClassesInfo
.size();
725 for ( size_t n
= 0; n
< count
; n
++ )
727 const ClassRegInfo
& regClass
= gs_regClassesInfo
[n
];
728 if ( !::UnregisterClass(regClass
.regname
.c_str(), wxGetInstance()) )
730 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
734 if ( !::UnregisterClass(regClass
.regnameNR
.c_str(), wxGetInstance()) )
736 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
737 regClass
.regnameNR
));
741 gs_regClassesInfo
.clear();
744 void wxApp::CleanUp()
746 // all objects pending for deletion must be deleted first, otherwise
747 // UnregisterWindowClasses() call wouldn't succeed (because windows
748 // using the classes being unregistered still exist), so call the base
749 // class method first and only then do our clean up
750 wxAppBase::CleanUp();
752 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
753 wxSetKeyboardHook(false);
758 // for an EXE the classes are unregistered when it terminates but DLL may
759 // be loaded several times (load/unload/load) into the same process in
760 // which case the registration will fail after the first time if we don't
761 // unregister the classes now
762 UnregisterWindowClasses();
765 // ----------------------------------------------------------------------------
767 // ----------------------------------------------------------------------------
771 m_printMode
= wxPRINT_WINDOWS
;
778 // ----------------------------------------------------------------------------
779 // wxApp idle handling
780 // ----------------------------------------------------------------------------
782 void wxApp::OnIdle(wxIdleEvent
& WXUNUSED(event
))
784 #if wxUSE_DC_CACHEING
785 // automated DC cache management: clear the cached DCs and bitmap
786 // if it's likely that the app has finished with them, that is, we
787 // get an idle event and we're not dragging anything.
788 if (!::GetKeyState(MK_LBUTTON
) && !::GetKeyState(MK_MBUTTON
) && !::GetKeyState(MK_RBUTTON
))
789 wxMSWDCImpl::ClearCache();
790 #endif // wxUSE_DC_CACHEING
793 void wxApp::WakeUpIdle()
795 // Send the top window a dummy message so idle handler processing will
796 // start up again. Doing it this way ensures that the idle handler
797 // wakes up in the right thread (see also wxWakeUpMainThread() which does
798 // the same for the main app thread only)
799 wxWindow
* const topWindow
= wxTheApp
->GetTopWindow();
802 HWND hwndTop
= GetHwndOf(topWindow
);
804 // Do not post WM_NULL if there's already a pending WM_NULL to avoid
805 // overflowing the message queue.
807 // Notice that due to a limitation of PeekMessage() API (which handles
808 // 0,0 range specially), we have to check the range from 0-1 instead.
809 // This still makes it possible to overflow the queue with WM_NULLs by
810 // interspersing the calles to WakeUpIdle() with windows creation but
811 // it should be rather hard to do it accidentally.
813 if ( !::PeekMessage(&msg
, hwndTop
, 0, 1, PM_NOREMOVE
) ||
814 ::PeekMessage(&msg
, hwndTop
, 1, 1, PM_NOREMOVE
) )
816 if ( !::PostMessage(hwndTop
, WM_NULL
, 0, 0) )
818 // should never happen
819 wxLogLastError(wxT("PostMessage(WM_NULL)"));
825 wxWakeUpMainThread();
826 #endif // wxUSE_THREADS
829 // ----------------------------------------------------------------------------
830 // other wxApp event hanlders
831 // ----------------------------------------------------------------------------
833 void wxApp::OnEndSession(wxCloseEvent
& WXUNUSED(event
))
835 // Windows will terminate the process soon after we return from
836 // WM_ENDSESSION handler or when we delete our last window, so make sure we
837 // at least execute our cleanup code before
839 // prevent the window from being destroyed when the corresponding wxTLW is
840 // destroyed: this will result in a leak of a HWND, of course, but who
841 // cares when the process is being killed anyhow
842 if ( !wxTopLevelWindows
.empty() )
843 wxTopLevelWindows
[0]->SetHWND(0);
845 const int rc
= OnExit();
849 // calling exit() instead of ExitProcess() or not doing anything at all and
850 // being killed by Windows has the advantage of executing the dtors of
855 // Default behaviour: close the application with prompts. The
856 // user can veto the close, and therefore the end session.
857 void wxApp::OnQueryEndSession(wxCloseEvent
& event
)
861 if (!GetTopWindow()->Close(!event
.CanVeto()))
866 // ----------------------------------------------------------------------------
867 // system DLL versions
868 // ----------------------------------------------------------------------------
870 // these functions have trivial inline implementations for CE
873 #if wxUSE_DYNLIB_CLASS
878 // helper function: retrieve the DLL version by using DllGetVersion(), returns
879 // 0 if the DLL doesn't export such function
880 int CallDllGetVersion(wxDynamicLibrary
& dll
)
882 // now check if the function is available during run-time
883 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC
, DllGetVersion
, dll
);
884 if ( !pfnDllGetVersion
)
888 dvi
.cbSize
= sizeof(dvi
);
890 HRESULT hr
= (*pfnDllGetVersion
)(&dvi
);
893 wxLogApiError(wxT("DllGetVersion"), hr
);
898 return 100*dvi
.dwMajorVersion
+ dvi
.dwMinorVersion
;
901 } // anonymous namespace
904 int wxApp::GetComCtl32Version()
908 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
909 // but as its value should be the same both times it doesn't matter
910 static int s_verComCtl32
= -1;
912 if ( s_verComCtl32
== -1 )
914 // we're prepared to handle the errors
917 // we don't want to load comctl32.dll, it should be already loaded but,
918 // depending on the OS version and the presence of the manifest, it can
919 // be either v5 or v6 and instead of trying to guess it just get the
920 // handle of the already loaded version
921 wxLoadedDLL
dllComCtl32(wxT("comctl32.dll"));
922 if ( !dllComCtl32
.IsLoaded() )
928 // try DllGetVersion() for recent DLLs
929 s_verComCtl32
= CallDllGetVersion(dllComCtl32
);
931 // if DllGetVersion() is unavailable either during compile or
932 // run-time, try to guess the version otherwise
933 if ( !s_verComCtl32
)
935 // InitCommonControlsEx is unique to 4.70 and later
936 void *pfn
= dllComCtl32
.GetSymbol(wxT("InitCommonControlsEx"));
939 // not found, must be 4.00
944 // many symbols appeared in comctl32 4.71, could use any of
945 // them except may be DllInstall()
946 pfn
= dllComCtl32
.GetSymbol(wxT("InitializeFlatSB"));
949 // not found, must be 4.70
954 // found, must be 4.71 or later
961 return s_verComCtl32
;
965 int wxApp::GetShell32Version()
967 static int s_verShell32
= -1;
968 if ( s_verShell32
== -1 )
970 // we're prepared to handle the errors
973 wxDynamicLibrary
dllShell32(wxT("shell32.dll"), wxDL_VERBATIM
);
974 if ( dllShell32
.IsLoaded() )
976 s_verShell32
= CallDllGetVersion(dllShell32
);
980 // there doesn't seem to be any way to distinguish between 4.00
981 // and 4.70 (starting from 4.71 we have DllGetVersion()) so
982 // just assume it is 4.0
986 else // failed load the DLL?
995 #else // !wxUSE_DYNLIB_CLASS
998 int wxApp::GetComCtl32Version()
1004 int wxApp::GetShell32Version()
1009 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
1011 #endif // !__WXWINCE__
1013 #if wxUSE_EXCEPTIONS
1015 // ----------------------------------------------------------------------------
1016 // exception handling
1017 // ----------------------------------------------------------------------------
1019 bool wxApp::OnExceptionInMainLoop()
1021 // ask the user about what to do: use the Win32 API function here as it
1022 // could be dangerous to use any wxWidgets code in this state
1027 wxT("An unhandled exception occurred. Press \"Abort\" to \
1028 terminate the program,\r\n\
1029 \"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
1030 wxT("Unhandled exception"),
1031 MB_ABORTRETRYIGNORE
|
1041 wxFAIL_MSG( wxT("unexpected MessageBox() return code") );
1052 #endif // wxUSE_EXCEPTIONS