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
222 bool wxGUIAppTraits::DoMessageFromThreadWait()
224 // we should return false only if the app should exit, i.e. only if
225 // Dispatch() determines that the main event loop should terminate
226 wxEventLoopBase
* const evtLoop
= wxEventLoop::GetActive();
227 if ( !evtLoop
|| !evtLoop
->Pending() )
229 // no events means no quit event
233 return evtLoop
->Dispatch();
236 DWORD
wxGUIAppTraits::WaitForThread(WXHANDLE hThread
, int flags
)
238 // We only ever dispatch messages from the main thread and, additionally,
239 // even from the main thread we shouldn't wait for the message if we don't
240 // have a running event loop as we would never remove them from the message
241 // queue then and so we would enter an infinite loop as
242 // MsgWaitForMultipleObjects() keeps returning WAIT_OBJECT_0 + 1.
243 if ( flags
== wxTHREAD_WAIT_BLOCK
||
245 !wxEventLoop::GetActive() )
247 // Simple blocking wait.
248 return DoSimpleWaitForThread(hThread
);
251 return ::MsgWaitForMultipleObjects
253 1, // number of objects to wait for
254 (HANDLE
*)&hThread
, // the objects
255 false, // wait for any objects, not all
256 INFINITE
, // no timeout
257 QS_ALLINPUT
| // return as soon as there are any events
262 wxPortId
wxGUIAppTraits::GetToolkitVersion(int *majVer
, int *minVer
) const
267 // on Windows, the toolkit version is the same of the OS version
268 // as Windows integrates the OS kernel with the GUI toolkit.
269 info
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
270 if ( ::GetVersionEx(&info
) )
273 *majVer
= info
.dwMajorVersion
;
275 *minVer
= info
.dwMinorVersion
;
278 #if defined(__WXHANDHELD__) || defined(__WXWINCE__)
287 wxTimerImpl
*wxGUIAppTraits::CreateTimerImpl(wxTimer
*timer
)
289 return new wxMSWTimerImpl(timer
);
292 #endif // wxUSE_TIMER
294 wxEventLoopBase
* wxGUIAppTraits::CreateEventLoop()
296 return new wxEventLoop
;
299 // ---------------------------------------------------------------------------
300 // Stuff for using console from the GUI applications
301 // ---------------------------------------------------------------------------
305 #if wxUSE_DYNLIB_CLASS
307 #include <wx/dynlib.h>
313 Helper class to manipulate console from a GUI app.
315 Notice that console output is available in the GUI app only if:
316 - AttachConsole() returns TRUE (which means it never works under pre-XP)
317 - we have a valid STD_ERROR_HANDLE
318 - command history hasn't been changed since our startup
320 To check if all these conditions are verified, you need to simple call
321 IsOkToUse(). It will check the first two conditions above the first time it
322 is called (and if this fails, the subsequent calls will return immediately)
323 and also recheck the last one every time it is called.
325 class wxConsoleStderr
328 // default ctor does nothing, call Init() before using this class
331 m_hStderr
= INVALID_HANDLE_VALUE
;
341 if ( m_hStderr
!= INVALID_HANDLE_VALUE
)
343 if ( !::FreeConsole() )
345 wxLogLastError(wxT("FreeConsole"));
350 // return true if we were successfully initialized and there had been no
351 // console activity which would interfere with our output since then
352 bool IsOkToUse() const
356 wxConsoleStderr
* const self
= const_cast<wxConsoleStderr
*>(this);
357 self
->m_ok
= self
->DoInit();
359 // no need to call IsHistoryUnchanged() as we just initialized
364 return m_ok
&& IsHistoryUnchanged();
368 // output the provided text on the console, return true if ok
369 bool Write(const wxString
& text
);
372 // called by Init() once only to do the real initialization
375 // retrieve the command line history into the provided buffer and return
377 int GetCommandHistory(wxWxCharBuffer
& buf
) const;
379 // check if the console history has changed
380 bool IsHistoryUnchanged() const;
382 int m_ok
; // initially -1, set to true or false by Init()
384 wxDynamicLibrary m_dllKernel32
;
386 HANDLE m_hStderr
; // console handle, if it's valid we must call
387 // FreeConsole() (even if m_ok != 1)
389 wxWxCharBuffer m_history
; // command history on startup
390 int m_historyLen
; // length command history buffer
392 wxCharBuffer m_data
; // data between empty line and cursor position
393 int m_dataLen
; // length data buffer
394 int m_dataLine
; // line offset
396 typedef DWORD (WINAPI
*GetConsoleCommandHistory_t
)(LPTSTR sCommands
,
399 typedef DWORD (WINAPI
*GetConsoleCommandHistoryLength_t
)(LPCTSTR sExeName
);
401 GetConsoleCommandHistory_t m_pfnGetConsoleCommandHistory
;
402 GetConsoleCommandHistoryLength_t m_pfnGetConsoleCommandHistoryLength
;
404 wxDECLARE_NO_COPY_CLASS(wxConsoleStderr
);
407 bool wxConsoleStderr::DoInit()
409 HANDLE hStderr
= ::GetStdHandle(STD_ERROR_HANDLE
);
411 if ( hStderr
== INVALID_HANDLE_VALUE
|| !hStderr
)
414 if ( !m_dllKernel32
.Load(wxT("kernel32.dll")) )
417 typedef BOOL (WINAPI
*AttachConsole_t
)(DWORD dwProcessId
);
418 AttachConsole_t
wxDL_INIT_FUNC(pfn
, AttachConsole
, m_dllKernel32
);
420 if ( !pfnAttachConsole
|| !pfnAttachConsole(ATTACH_PARENT_PROCESS
) )
423 // console attached, set m_hStderr now to ensure that we free it in the
427 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistory
, m_dllKernel32
);
428 if ( !m_pfnGetConsoleCommandHistory
)
431 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistoryLength
, m_dllKernel32
);
432 if ( !m_pfnGetConsoleCommandHistoryLength
)
435 // remember the current command history to be able to compare with it later
436 // in IsHistoryUnchanged()
437 m_historyLen
= GetCommandHistory(m_history
);
442 // now find the first blank line above the current position
443 CONSOLE_SCREEN_BUFFER_INFO csbi
;
445 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
447 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
453 pos
.Y
= csbi
.dwCursorPosition
.Y
+ 1;
455 // we decide that a line is empty if first 4 characters are spaces
461 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, buf
, WXSIZEOF(buf
),
464 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
467 } while ( wxStrncmp(" ", buf
, WXSIZEOF(buf
)) != 0 );
469 // calculate line offset and length of data
470 m_dataLine
= csbi
.dwCursorPosition
.Y
- pos
.Y
;
471 m_dataLen
= m_dataLine
*csbi
.dwMaximumWindowSize
.X
+ csbi
.dwCursorPosition
.X
;
475 m_data
.extend(m_dataLen
);
476 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, m_data
.data(), m_dataLen
,
479 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
487 int wxConsoleStderr::GetCommandHistory(wxWxCharBuffer
& buf
) const
489 // these functions are internal and may only be called by cmd.exe
490 static const wxChar
*CMD_EXE
= wxT("cmd.exe");
492 const int len
= m_pfnGetConsoleCommandHistoryLength(CMD_EXE
);
497 int len2
= m_pfnGetConsoleCommandHistory(buf
.data(), len
, CMD_EXE
);
500 // there seems to be a bug in the GetConsoleCommandHistoryA(), it
501 // returns the length of Unicode string and not ANSI one
503 #endif // !wxUSE_UNICODE
507 wxFAIL_MSG( wxT("failed getting history?") );
514 bool wxConsoleStderr::IsHistoryUnchanged() const
516 wxASSERT_MSG( m_ok
== 1, wxT("shouldn't be called if not initialized") );
518 // get (possibly changed) command history
519 wxWxCharBuffer history
;
520 const int historyLen
= GetCommandHistory(history
);
522 // and compare it with the original one
523 return historyLen
== m_historyLen
&& history
&&
524 memcmp(m_history
, history
, historyLen
) == 0;
527 bool wxConsoleStderr::Write(const wxString
& text
)
529 wxASSERT_MSG( m_hStderr
!= INVALID_HANDLE_VALUE
,
530 wxT("should only be called if Init() returned true") );
532 // get current position
533 CONSOLE_SCREEN_BUFFER_INFO csbi
;
534 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
536 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
540 // and calculate new position (where is empty line)
541 csbi
.dwCursorPosition
.X
= 0;
542 csbi
.dwCursorPosition
.Y
-= m_dataLine
;
544 if ( !::SetConsoleCursorPosition(m_hStderr
, csbi
.dwCursorPosition
) )
546 wxLogLastError(wxT("SetConsoleCursorPosition"));
551 if ( !::FillConsoleOutputCharacter(m_hStderr
, wxT(' '), m_dataLen
,
552 csbi
.dwCursorPosition
, &ret
) )
554 wxLogLastError(wxT("FillConsoleOutputCharacter"));
558 if ( !::WriteConsole(m_hStderr
, text
.wx_str(), text
.length(), &ret
, NULL
) )
560 wxLogLastError(wxT("WriteConsole"));
564 WriteConsoleA(m_hStderr
, m_data
, m_dataLen
, &ret
, 0);
569 wxConsoleStderr s_consoleStderr
;
571 } // anonymous namespace
573 bool wxGUIAppTraits::CanUseStderr()
575 return s_consoleStderr
.IsOkToUse();
578 bool wxGUIAppTraits::WriteToStderr(const wxString
& text
)
580 return s_consoleStderr
.IsOkToUse() && s_consoleStderr
.Write(text
);
583 #else // !wxUSE_DYNLIB_CLASS
585 bool wxGUIAppTraits::CanUseStderr()
590 bool wxGUIAppTraits::WriteToStderr(const wxString
& WXUNUSED(text
))
595 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
597 #endif // !__WXWINCE__
599 // ===========================================================================
600 // wxApp implementation
601 // ===========================================================================
603 int wxApp::m_nCmdShow
= SW_SHOWNORMAL
;
605 // ---------------------------------------------------------------------------
607 // ---------------------------------------------------------------------------
609 IMPLEMENT_DYNAMIC_CLASS(wxApp
, wxEvtHandler
)
611 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
612 EVT_IDLE(wxApp::OnIdle
)
613 EVT_END_SESSION(wxApp::OnEndSession
)
614 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession
)
617 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
619 class wxCallBaseCleanup
622 wxCallBaseCleanup(wxApp
*app
) : m_app(app
) { }
623 ~wxCallBaseCleanup() { if ( m_app
) m_app
->wxAppBase::CleanUp(); }
625 void Dismiss() { m_app
= NULL
; }
632 bool wxApp::Initialize(int& argc
, wxChar
**argv
)
634 if ( !wxAppBase::Initialize(argc
, argv
) )
637 // ensure that base cleanup is done if we return too early
638 wxCallBaseCleanup
callBaseCleanup(this);
640 #if !defined(__WXMICROWIN__)
641 InitCommonControls();
642 #endif // !defined(__WXMICROWIN__)
644 #if defined(__SMARTPHONE__) || defined(__POCKETPC__)
645 SHInitExtraControls();
649 // Don't show a message box if a function such as SHGetFileInfo
650 // fails to find a device.
651 SetErrorMode(SEM_FAILCRITICALERRORS
|SEM_NOOPENFILEERRORBOX
);
656 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
657 wxSetKeyboardHook(true);
660 callBaseCleanup
.Dismiss();
665 // ---------------------------------------------------------------------------
666 // Win32 window class registration
667 // ---------------------------------------------------------------------------
670 const wxChar
*wxApp::GetRegisteredClassName(const wxChar
*name
,
674 const size_t count
= gs_regClassesInfo
.size();
675 for ( size_t n
= 0; n
< count
; n
++ )
677 if ( gs_regClassesInfo
[n
].regname
== name
)
678 return gs_regClassesInfo
[n
].regname
.c_str();
681 // we need to register this class
683 wxZeroMemory(wndclass
);
685 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
686 wndclass
.hInstance
= wxGetInstance();
687 wndclass
.hCursor
= ::LoadCursor(NULL
, IDC_ARROW
);
688 wndclass
.hbrBackground
= (HBRUSH
)wxUIntToPtr(bgBrushCol
+ 1);
689 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| extraStyles
;
692 ClassRegInfo
regClass(name
);
693 wndclass
.lpszClassName
= regClass
.regname
.wx_str();
694 if ( !::RegisterClass(&wndclass
) )
696 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
701 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
702 wndclass
.lpszClassName
= regClass
.regnameNR
.wx_str();
703 if ( !::RegisterClass(&wndclass
) )
705 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
707 ::UnregisterClass(regClass
.regname
.c_str(), wxGetInstance());
711 gs_regClassesInfo
.push_back(regClass
);
713 // take care to return the pointer which will remain valid after the
714 // function returns (it could be invalidated later if new elements are
715 // added to the vector and it's reallocated but this shouldn't matter as
716 // this pointer should be used right now, not stored)
717 return gs_regClassesInfo
.back().regname
.wx_str();
720 bool wxApp::IsRegisteredClassName(const wxString
& name
)
722 const size_t count
= gs_regClassesInfo
.size();
723 for ( size_t n
= 0; n
< count
; n
++ )
725 if ( gs_regClassesInfo
[n
].regname
== name
||
726 gs_regClassesInfo
[n
].regnameNR
== name
)
733 void wxApp::UnregisterWindowClasses()
735 const size_t count
= gs_regClassesInfo
.size();
736 for ( size_t n
= 0; n
< count
; n
++ )
738 const ClassRegInfo
& regClass
= gs_regClassesInfo
[n
];
739 if ( !::UnregisterClass(regClass
.regname
.c_str(), wxGetInstance()) )
741 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
745 if ( !::UnregisterClass(regClass
.regnameNR
.c_str(), wxGetInstance()) )
747 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
748 regClass
.regnameNR
));
752 gs_regClassesInfo
.clear();
755 void wxApp::CleanUp()
757 // all objects pending for deletion must be deleted first, otherwise
758 // UnregisterWindowClasses() call wouldn't succeed (because windows
759 // using the classes being unregistered still exist), so call the base
760 // class method first and only then do our clean up
761 wxAppBase::CleanUp();
763 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
764 wxSetKeyboardHook(false);
769 // for an EXE the classes are unregistered when it terminates but DLL may
770 // be loaded several times (load/unload/load) into the same process in
771 // which case the registration will fail after the first time if we don't
772 // unregister the classes now
773 UnregisterWindowClasses();
776 // ----------------------------------------------------------------------------
778 // ----------------------------------------------------------------------------
782 m_printMode
= wxPRINT_WINDOWS
;
789 // ----------------------------------------------------------------------------
790 // wxApp idle handling
791 // ----------------------------------------------------------------------------
793 void wxApp::OnIdle(wxIdleEvent
& WXUNUSED(event
))
795 #if wxUSE_DC_CACHEING
796 // automated DC cache management: clear the cached DCs and bitmap
797 // if it's likely that the app has finished with them, that is, we
798 // get an idle event and we're not dragging anything.
799 if (!::GetKeyState(MK_LBUTTON
) && !::GetKeyState(MK_MBUTTON
) && !::GetKeyState(MK_RBUTTON
))
800 wxMSWDCImpl::ClearCache();
801 #endif // wxUSE_DC_CACHEING
804 void wxApp::WakeUpIdle()
806 // Send the top window a dummy message so idle handler processing will
807 // start up again. Doing it this way ensures that the idle handler
808 // wakes up in the right thread (see also wxWakeUpMainThread() which does
809 // the same for the main app thread only)
810 wxWindow
* const topWindow
= wxTheApp
->GetTopWindow();
813 HWND hwndTop
= GetHwndOf(topWindow
);
815 // Do not post WM_NULL if there's already a pending WM_NULL to avoid
816 // overflowing the message queue.
818 // Notice that due to a limitation of PeekMessage() API (which handles
819 // 0,0 range specially), we have to check the range from 0-1 instead.
820 // This still makes it possible to overflow the queue with WM_NULLs by
821 // interspersing the calles to WakeUpIdle() with windows creation but
822 // it should be rather hard to do it accidentally.
824 if ( !::PeekMessage(&msg
, hwndTop
, 0, 1, PM_NOREMOVE
) ||
825 ::PeekMessage(&msg
, hwndTop
, 1, 1, PM_NOREMOVE
) )
827 if ( !::PostMessage(hwndTop
, WM_NULL
, 0, 0) )
829 // should never happen
830 wxLogLastError(wxT("PostMessage(WM_NULL)"));
836 wxWakeUpMainThread();
837 #endif // wxUSE_THREADS
840 // ----------------------------------------------------------------------------
841 // other wxApp event hanlders
842 // ----------------------------------------------------------------------------
844 void wxApp::OnEndSession(wxCloseEvent
& WXUNUSED(event
))
846 // Windows will terminate the process soon after we return from
847 // WM_ENDSESSION handler or when we delete our last window, so make sure we
848 // at least execute our cleanup code before
850 // prevent the window from being destroyed when the corresponding wxTLW is
851 // destroyed: this will result in a leak of a HWND, of course, but who
852 // cares when the process is being killed anyhow
853 if ( !wxTopLevelWindows
.empty() )
854 wxTopLevelWindows
[0]->SetHWND(0);
856 const int rc
= OnExit();
860 // calling exit() instead of ExitProcess() or not doing anything at all and
861 // being killed by Windows has the advantage of executing the dtors of
866 // Default behaviour: close the application with prompts. The
867 // user can veto the close, and therefore the end session.
868 void wxApp::OnQueryEndSession(wxCloseEvent
& event
)
872 if (!GetTopWindow()->Close(!event
.CanVeto()))
877 // ----------------------------------------------------------------------------
878 // system DLL versions
879 // ----------------------------------------------------------------------------
881 // these functions have trivial inline implementations for CE
884 #if wxUSE_DYNLIB_CLASS
889 // helper function: retrieve the DLL version by using DllGetVersion(), returns
890 // 0 if the DLL doesn't export such function
891 int CallDllGetVersion(wxDynamicLibrary
& dll
)
893 // now check if the function is available during run-time
894 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC
, DllGetVersion
, dll
);
895 if ( !pfnDllGetVersion
)
899 dvi
.cbSize
= sizeof(dvi
);
901 HRESULT hr
= (*pfnDllGetVersion
)(&dvi
);
904 wxLogApiError(wxT("DllGetVersion"), hr
);
909 return 100*dvi
.dwMajorVersion
+ dvi
.dwMinorVersion
;
912 } // anonymous namespace
915 int wxApp::GetComCtl32Version()
919 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
920 // but as its value should be the same both times it doesn't matter
921 static int s_verComCtl32
= -1;
923 if ( s_verComCtl32
== -1 )
925 // we're prepared to handle the errors
928 // we don't want to load comctl32.dll, it should be already loaded but,
929 // depending on the OS version and the presence of the manifest, it can
930 // be either v5 or v6 and instead of trying to guess it just get the
931 // handle of the already loaded version
932 wxLoadedDLL
dllComCtl32(wxT("comctl32.dll"));
933 if ( !dllComCtl32
.IsLoaded() )
939 // try DllGetVersion() for recent DLLs
940 s_verComCtl32
= CallDllGetVersion(dllComCtl32
);
942 // if DllGetVersion() is unavailable either during compile or
943 // run-time, try to guess the version otherwise
944 if ( !s_verComCtl32
)
946 // InitCommonControlsEx is unique to 4.70 and later
947 void *pfn
= dllComCtl32
.GetSymbol(wxT("InitCommonControlsEx"));
950 // not found, must be 4.00
955 // many symbols appeared in comctl32 4.71, could use any of
956 // them except may be DllInstall()
957 pfn
= dllComCtl32
.GetSymbol(wxT("InitializeFlatSB"));
960 // not found, must be 4.70
965 // found, must be 4.71 or later
972 return s_verComCtl32
;
976 int wxApp::GetShell32Version()
978 static int s_verShell32
= -1;
979 if ( s_verShell32
== -1 )
981 // we're prepared to handle the errors
984 wxDynamicLibrary
dllShell32(wxT("shell32.dll"), wxDL_VERBATIM
);
985 if ( dllShell32
.IsLoaded() )
987 s_verShell32
= CallDllGetVersion(dllShell32
);
991 // there doesn't seem to be any way to distinguish between 4.00
992 // and 4.70 (starting from 4.71 we have DllGetVersion()) so
993 // just assume it is 4.0
997 else // failed load the DLL?
1003 return s_verShell32
;
1006 #else // !wxUSE_DYNLIB_CLASS
1009 int wxApp::GetComCtl32Version()
1015 int wxApp::GetShell32Version()
1020 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
1022 #endif // !__WXWINCE__
1024 #if wxUSE_EXCEPTIONS
1026 // ----------------------------------------------------------------------------
1027 // exception handling
1028 // ----------------------------------------------------------------------------
1030 bool wxApp::OnExceptionInMainLoop()
1032 // ask the user about what to do: use the Win32 API function here as it
1033 // could be dangerous to use any wxWidgets code in this state
1038 wxT("An unhandled exception occurred. Press \"Abort\" to \
1039 terminate the program,\r\n\
1040 \"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
1041 wxT("Unhandled exception"),
1042 MB_ABORTRETRYIGNORE
|
1052 wxFAIL_MSG( wxT("unexpected MessageBox() return code") );
1063 #endif // wxUSE_EXCEPTIONS