1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/app.cpp
4 // Author: Julian Smart
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ===========================================================================
13 // ===========================================================================
15 // ---------------------------------------------------------------------------
17 // ---------------------------------------------------------------------------
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
22 #if defined(__BORLANDC__)
27 #include "wx/msw/wrapcctl.h"
28 #include "wx/dynarray.h"
32 #include "wx/gdicmn.h"
35 #include "wx/cursor.h"
37 #include "wx/palette.h"
39 #include "wx/dialog.h"
40 #include "wx/msgdlg.h"
44 #include "wx/module.h"
47 #include "wx/apptrait.h"
48 #include "wx/filename.h"
49 #include "wx/dynlib.h"
50 #include "wx/evtloop.h"
51 #include "wx/thread.h"
52 #include "wx/scopeguard.h"
53 #include "wx/vector.h"
55 #include "wx/msw/private.h"
56 #include "wx/msw/dc.h"
57 #include "wx/msw/ole/oleutils.h"
58 #include "wx/msw/private/timer.h"
61 #include "wx/tooltip.h"
62 #endif // wxUSE_TOOLTIPS
64 // OLE is used for drag-and-drop, clipboard, OLE Automation..., but some
65 // compilers don't support it (missing headers, libs, ...)
66 #if defined(__GNUWIN32_OLD__) || defined(__SYMANTEC__)
70 #endif // broken compilers
72 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
84 #include "wx/msw/missing.h"
86 // instead of including <shlwapi.h> which is not part of the core SDK and not
87 // shipped at all with other compilers, we always define the parts of it we
88 // need here ourselves
90 // NB: DLLVER_PLATFORM_WINDOWS will be defined if shlwapi.h had been somehow
92 #ifndef DLLVER_PLATFORM_WINDOWS
93 // hopefully we don't need to change packing as DWORDs should be already
98 DWORD dwMajorVersion
; // Major version
99 DWORD dwMinorVersion
; // Minor version
100 DWORD dwBuildNumber
; // Build number
101 DWORD dwPlatformID
; // DLLVER_PLATFORM_*
104 typedef HRESULT (CALLBACK
* DLLGETVERSIONPROC
)(DLLVERSIONINFO
*);
105 #endif // defined(DLLVERSIONINFO)
107 #ifndef ATTACH_PARENT_PROCESS
108 #define ATTACH_PARENT_PROCESS ((DWORD)-1)
111 // ---------------------------------------------------------------------------
113 // ---------------------------------------------------------------------------
115 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
116 extern void wxSetKeyboardHook(bool doIt
);
119 // because of mingw32 4.3 bug this struct can't be inside the namespace below:
120 // see http://article.gmane.org/gmane.comp.lib.wxwidgets.devel/110282
123 ClassRegInfo(const wxChar
*name
)
125 regnameNR(regname
+ wxApp::GetNoRedrawClassSuffix())
129 // the name of the registered class with and without CS_[HV]REDRAW styles
137 wxVector
<ClassRegInfo
> gs_regClassesInfo
;
139 } // anonymous namespace
141 // ----------------------------------------------------------------------------
143 // ----------------------------------------------------------------------------
145 LRESULT WXDLLEXPORT APIENTRY
wxWndProc(HWND
, UINT
, WPARAM
, LPARAM
);
147 // ===========================================================================
148 // wxGUIAppTraits implementation
149 // ===========================================================================
151 // private class which we use to pass parameters from BeforeChildWaitLoop() to
152 // AfterChildWaitLoop()
153 struct ChildWaitLoopData
155 ChildWaitLoopData(wxWindowDisabler
*wd_
, wxWindow
*winActive_
)
158 winActive
= winActive_
;
161 wxWindowDisabler
*wd
;
165 void *wxGUIAppTraits::BeforeChildWaitLoop()
168 We use a dirty hack here to disable all application windows (which we
169 must do because otherwise the calls to wxYield() could lead to some very
170 unexpected reentrancies in the users code) but to avoid losing
171 focus/activation entirely when the child process terminates which would
172 happen if we simply disabled everything using wxWindowDisabler. Indeed,
173 remember that Windows will never activate a disabled window and when the
174 last child's window is closed and Windows looks for a window to activate
175 all our windows are still disabled. There is no way to enable them in
176 time because we don't know when the child's windows are going to be
177 closed, so the solution we use here is to keep one special tiny dialog
178 enabled all the time. Then when the child terminates it will get
179 activated and when we close it below -- after re-enabling all the other
180 windows! -- the previously active window becomes activated again and
185 // first disable all existing windows
186 wxWindowDisabler
*wd
= new wxWindowDisabler
;
188 // then create an "invisible" dialog: it has minimal size, is positioned
189 // (hopefully) outside the screen and doesn't appear in the Alt-TAB list
190 // (unlike the frames, which is why we use a dialog here)
191 wxWindow
*winActive
= new wxDialog
193 wxTheApp
->GetTopWindow(),
196 wxPoint(32600, 32600),
201 return new ChildWaitLoopData(wd
, winActive
);
204 void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig
)
208 ChildWaitLoopData
* const data
= (ChildWaitLoopData
*)dataOrig
;
212 // finally delete the dummy dialog and, as wd has been already destroyed
213 // and the other windows reenabled, the activation is going to return to
214 // the window which had had it before
215 data
->winActive
->Destroy();
217 // 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
261 #endif // wxUSE_THREADS
263 wxPortId
wxGUIAppTraits::GetToolkitVersion(int *majVer
, int *minVer
) const
268 // on Windows, the toolkit version is the same of the OS version
269 // as Windows integrates the OS kernel with the GUI toolkit.
270 info
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
271 if ( ::GetVersionEx(&info
) )
274 *majVer
= info
.dwMajorVersion
;
276 *minVer
= info
.dwMinorVersion
;
279 #if defined(__WXHANDHELD__) || defined(__WXWINCE__)
288 wxTimerImpl
*wxGUIAppTraits::CreateTimerImpl(wxTimer
*timer
)
290 return new wxMSWTimerImpl(timer
);
293 #endif // wxUSE_TIMER
295 wxEventLoopBase
* wxGUIAppTraits::CreateEventLoop()
297 return new wxEventLoop
;
300 // ---------------------------------------------------------------------------
301 // Stuff for using console from the GUI applications
302 // ---------------------------------------------------------------------------
306 #if wxUSE_DYNLIB_CLASS
308 #include <wx/dynlib.h>
314 Helper class to manipulate console from a GUI app.
316 Notice that console output is available in the GUI app only if:
317 - AttachConsole() returns TRUE (which means it never works under pre-XP)
318 - we have a valid STD_ERROR_HANDLE
319 - command history hasn't been changed since our startup
321 To check if all these conditions are verified, you need to simple call
322 IsOkToUse(). It will check the first two conditions above the first time it
323 is called (and if this fails, the subsequent calls will return immediately)
324 and also recheck the last one every time it is called.
326 class wxConsoleStderr
329 // default ctor does nothing, call Init() before using this class
332 m_hStderr
= INVALID_HANDLE_VALUE
;
342 if ( m_hStderr
!= INVALID_HANDLE_VALUE
)
344 if ( !::FreeConsole() )
346 wxLogLastError(wxT("FreeConsole"));
351 // return true if we were successfully initialized and there had been no
352 // console activity which would interfere with our output since then
353 bool IsOkToUse() const
357 wxConsoleStderr
* const self
= const_cast<wxConsoleStderr
*>(this);
358 self
->m_ok
= self
->DoInit();
360 // no need to call IsHistoryUnchanged() as we just initialized
365 return m_ok
&& IsHistoryUnchanged();
369 // output the provided text on the console, return true if ok
370 bool Write(const wxString
& text
);
373 // called by Init() once only to do the real initialization
376 // retrieve the command line history into the provided buffer and return
378 int GetCommandHistory(wxWxCharBuffer
& buf
) const;
380 // check if the console history has changed
381 bool IsHistoryUnchanged() const;
383 int m_ok
; // initially -1, set to true or false by Init()
385 wxDynamicLibrary m_dllKernel32
;
387 HANDLE m_hStderr
; // console handle, if it's valid we must call
388 // FreeConsole() (even if m_ok != 1)
390 wxWxCharBuffer m_history
; // command history on startup
391 int m_historyLen
; // length command history buffer
393 wxCharBuffer m_data
; // data between empty line and cursor position
394 int m_dataLen
; // length data buffer
395 int m_dataLine
; // line offset
397 typedef DWORD (WINAPI
*GetConsoleCommandHistory_t
)(LPTSTR sCommands
,
400 typedef DWORD (WINAPI
*GetConsoleCommandHistoryLength_t
)(LPCTSTR sExeName
);
402 GetConsoleCommandHistory_t m_pfnGetConsoleCommandHistory
;
403 GetConsoleCommandHistoryLength_t m_pfnGetConsoleCommandHistoryLength
;
405 wxDECLARE_NO_COPY_CLASS(wxConsoleStderr
);
408 bool wxConsoleStderr::DoInit()
410 HANDLE hStderr
= ::GetStdHandle(STD_ERROR_HANDLE
);
412 if ( hStderr
== INVALID_HANDLE_VALUE
|| !hStderr
)
415 if ( !m_dllKernel32
.Load(wxT("kernel32.dll")) )
418 typedef BOOL (WINAPI
*AttachConsole_t
)(DWORD dwProcessId
);
419 AttachConsole_t
wxDL_INIT_FUNC(pfn
, AttachConsole
, m_dllKernel32
);
421 if ( !pfnAttachConsole
|| !pfnAttachConsole(ATTACH_PARENT_PROCESS
) )
424 // console attached, set m_hStderr now to ensure that we free it in the
428 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistory
, m_dllKernel32
);
429 if ( !m_pfnGetConsoleCommandHistory
)
432 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistoryLength
, m_dllKernel32
);
433 if ( !m_pfnGetConsoleCommandHistoryLength
)
436 // remember the current command history to be able to compare with it later
437 // in IsHistoryUnchanged()
438 m_historyLen
= GetCommandHistory(m_history
);
443 // now find the first blank line above the current position
444 CONSOLE_SCREEN_BUFFER_INFO csbi
;
446 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
448 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
454 pos
.Y
= csbi
.dwCursorPosition
.Y
+ 1;
456 // we decide that a line is empty if first 4 characters are spaces
462 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, buf
, WXSIZEOF(buf
),
465 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
468 } while ( wxStrncmp(" ", buf
, WXSIZEOF(buf
)) != 0 );
470 // calculate line offset and length of data
471 m_dataLine
= csbi
.dwCursorPosition
.Y
- pos
.Y
;
472 m_dataLen
= m_dataLine
*csbi
.dwMaximumWindowSize
.X
+ csbi
.dwCursorPosition
.X
;
476 m_data
.extend(m_dataLen
);
477 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, m_data
.data(), m_dataLen
,
480 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
488 int wxConsoleStderr::GetCommandHistory(wxWxCharBuffer
& buf
) const
490 // these functions are internal and may only be called by cmd.exe
491 static const wxChar
*CMD_EXE
= wxT("cmd.exe");
493 const int len
= m_pfnGetConsoleCommandHistoryLength(CMD_EXE
);
498 int len2
= m_pfnGetConsoleCommandHistory(buf
.data(), len
, CMD_EXE
);
501 // there seems to be a bug in the GetConsoleCommandHistoryA(), it
502 // returns the length of Unicode string and not ANSI one
504 #endif // !wxUSE_UNICODE
508 wxFAIL_MSG( wxT("failed getting history?") );
515 bool wxConsoleStderr::IsHistoryUnchanged() const
517 wxASSERT_MSG( m_ok
== 1, wxT("shouldn't be called if not initialized") );
519 // get (possibly changed) command history
520 wxWxCharBuffer history
;
521 const int historyLen
= GetCommandHistory(history
);
523 // and compare it with the original one
524 return historyLen
== m_historyLen
&& history
&&
525 memcmp(m_history
, history
, historyLen
) == 0;
528 bool wxConsoleStderr::Write(const wxString
& text
)
530 wxASSERT_MSG( m_hStderr
!= INVALID_HANDLE_VALUE
,
531 wxT("should only be called if Init() returned true") );
533 // get current position
534 CONSOLE_SCREEN_BUFFER_INFO csbi
;
535 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
537 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
541 // and calculate new position (where is empty line)
542 csbi
.dwCursorPosition
.X
= 0;
543 csbi
.dwCursorPosition
.Y
-= m_dataLine
;
545 if ( !::SetConsoleCursorPosition(m_hStderr
, csbi
.dwCursorPosition
) )
547 wxLogLastError(wxT("SetConsoleCursorPosition"));
552 if ( !::FillConsoleOutputCharacter(m_hStderr
, wxT(' '), m_dataLen
,
553 csbi
.dwCursorPosition
, &ret
) )
555 wxLogLastError(wxT("FillConsoleOutputCharacter"));
559 if ( !::WriteConsole(m_hStderr
, text
.t_str(), text
.length(), &ret
, NULL
) )
561 wxLogLastError(wxT("WriteConsole"));
565 WriteConsoleA(m_hStderr
, m_data
, m_dataLen
, &ret
, 0);
570 wxConsoleStderr s_consoleStderr
;
572 } // anonymous namespace
574 bool wxGUIAppTraits::CanUseStderr()
576 return s_consoleStderr
.IsOkToUse();
579 bool wxGUIAppTraits::WriteToStderr(const wxString
& text
)
581 return s_consoleStderr
.IsOkToUse() && s_consoleStderr
.Write(text
);
584 #else // !wxUSE_DYNLIB_CLASS
586 bool wxGUIAppTraits::CanUseStderr()
591 bool wxGUIAppTraits::WriteToStderr(const wxString
& WXUNUSED(text
))
596 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
598 #endif // !__WXWINCE__
600 // ===========================================================================
601 // wxApp implementation
602 // ===========================================================================
604 int wxApp::m_nCmdShow
= SW_SHOWNORMAL
;
606 // ---------------------------------------------------------------------------
608 // ---------------------------------------------------------------------------
610 IMPLEMENT_DYNAMIC_CLASS(wxApp
, wxEvtHandler
)
612 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
613 EVT_IDLE(wxApp::OnIdle
)
614 EVT_END_SESSION(wxApp::OnEndSession
)
615 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession
)
618 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
620 class wxCallBaseCleanup
623 wxCallBaseCleanup(wxApp
*app
) : m_app(app
) { }
624 ~wxCallBaseCleanup() { if ( m_app
) m_app
->wxAppBase::CleanUp(); }
626 void Dismiss() { m_app
= NULL
; }
633 bool wxApp::Initialize(int& argc
, wxChar
**argv
)
635 if ( !wxAppBase::Initialize(argc
, argv
) )
638 // ensure that base cleanup is done if we return too early
639 wxCallBaseCleanup
callBaseCleanup(this);
641 #if !defined(__WXMICROWIN__)
642 InitCommonControls();
643 #endif // !defined(__WXMICROWIN__)
645 #if defined(__SMARTPHONE__) || defined(__POCKETPC__)
646 SHInitExtraControls();
650 // Don't show a message box if a function such as SHGetFileInfo
651 // fails to find a device.
652 SetErrorMode(SEM_FAILCRITICALERRORS
|SEM_NOOPENFILEERRORBOX
);
657 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
658 wxSetKeyboardHook(true);
661 callBaseCleanup
.Dismiss();
666 // ---------------------------------------------------------------------------
667 // Win32 window class registration
668 // ---------------------------------------------------------------------------
671 const wxChar
*wxApp::GetRegisteredClassName(const wxChar
*name
,
675 const size_t count
= gs_regClassesInfo
.size();
676 for ( size_t n
= 0; n
< count
; n
++ )
678 if ( gs_regClassesInfo
[n
].regname
== name
)
679 return gs_regClassesInfo
[n
].regname
.c_str();
682 // we need to register this class
684 wxZeroMemory(wndclass
);
686 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
687 wndclass
.hInstance
= wxGetInstance();
688 wndclass
.hCursor
= ::LoadCursor(NULL
, IDC_ARROW
);
689 wndclass
.hbrBackground
= (HBRUSH
)wxUIntToPtr(bgBrushCol
+ 1);
690 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| extraStyles
;
693 ClassRegInfo
regClass(name
);
694 wndclass
.lpszClassName
= regClass
.regname
.t_str();
695 if ( !::RegisterClass(&wndclass
) )
697 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
702 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
703 wndclass
.lpszClassName
= regClass
.regnameNR
.t_str();
704 if ( !::RegisterClass(&wndclass
) )
706 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
708 ::UnregisterClass(regClass
.regname
.c_str(), wxGetInstance());
712 gs_regClassesInfo
.push_back(regClass
);
714 // take care to return the pointer which will remain valid after the
715 // function returns (it could be invalidated later if new elements are
716 // added to the vector and it's reallocated but this shouldn't matter as
717 // this pointer should be used right now, not stored)
718 return gs_regClassesInfo
.back().regname
.t_str();
721 bool wxApp::IsRegisteredClassName(const wxString
& name
)
723 const size_t count
= gs_regClassesInfo
.size();
724 for ( size_t n
= 0; n
< count
; n
++ )
726 if ( gs_regClassesInfo
[n
].regname
== name
||
727 gs_regClassesInfo
[n
].regnameNR
== name
)
734 void wxApp::UnregisterWindowClasses()
736 const size_t count
= gs_regClassesInfo
.size();
737 for ( size_t n
= 0; n
< count
; n
++ )
739 const ClassRegInfo
& regClass
= gs_regClassesInfo
[n
];
740 if ( !::UnregisterClass(regClass
.regname
.c_str(), wxGetInstance()) )
742 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
746 if ( !::UnregisterClass(regClass
.regnameNR
.c_str(), wxGetInstance()) )
748 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
749 regClass
.regnameNR
));
753 gs_regClassesInfo
.clear();
756 void wxApp::CleanUp()
758 // all objects pending for deletion must be deleted first, otherwise
759 // UnregisterWindowClasses() call wouldn't succeed (because windows
760 // using the classes being unregistered still exist), so call the base
761 // class method first and only then do our clean up
762 wxAppBase::CleanUp();
764 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
765 wxSetKeyboardHook(false);
770 // for an EXE the classes are unregistered when it terminates but DLL may
771 // be loaded several times (load/unload/load) into the same process in
772 // which case the registration will fail after the first time if we don't
773 // unregister the classes now
774 UnregisterWindowClasses();
777 // ----------------------------------------------------------------------------
779 // ----------------------------------------------------------------------------
783 m_printMode
= wxPRINT_WINDOWS
;
790 // ----------------------------------------------------------------------------
791 // wxApp idle handling
792 // ----------------------------------------------------------------------------
794 void wxApp::OnIdle(wxIdleEvent
& WXUNUSED(event
))
796 #if wxUSE_DC_CACHEING
797 // automated DC cache management: clear the cached DCs and bitmap
798 // if it's likely that the app has finished with them, that is, we
799 // get an idle event and we're not dragging anything.
800 if (!::GetKeyState(MK_LBUTTON
) && !::GetKeyState(MK_MBUTTON
) && !::GetKeyState(MK_RBUTTON
))
801 wxMSWDCImpl::ClearCache();
802 #endif // wxUSE_DC_CACHEING
805 void wxApp::WakeUpIdle()
807 // Send the top window a dummy message so idle handler processing will
808 // start up again. Doing it this way ensures that the idle handler
809 // wakes up in the right thread (see also wxWakeUpMainThread() which does
810 // the same for the main app thread only)
811 wxWindow
* const topWindow
= wxTheApp
->GetTopWindow();
814 HWND hwndTop
= GetHwndOf(topWindow
);
816 // Do not post WM_NULL if there's already a pending WM_NULL to avoid
817 // overflowing the message queue.
819 // Notice that due to a limitation of PeekMessage() API (which handles
820 // 0,0 range specially), we have to check the range from 0-1 instead.
821 // This still makes it possible to overflow the queue with WM_NULLs by
822 // interspersing the calles to WakeUpIdle() with windows creation but
823 // it should be rather hard to do it accidentally.
825 if ( !::PeekMessage(&msg
, hwndTop
, 0, 1, PM_NOREMOVE
) ||
826 ::PeekMessage(&msg
, hwndTop
, 1, 1, PM_NOREMOVE
) )
828 if ( !::PostMessage(hwndTop
, WM_NULL
, 0, 0) )
830 // should never happen
831 wxLogLastError(wxT("PostMessage(WM_NULL)"));
837 wxWakeUpMainThread();
838 #endif // wxUSE_THREADS
841 // ----------------------------------------------------------------------------
842 // other wxApp event hanlders
843 // ----------------------------------------------------------------------------
845 void wxApp::OnEndSession(wxCloseEvent
& WXUNUSED(event
))
847 // Windows will terminate the process soon after we return from
848 // WM_ENDSESSION handler or when we delete our last window, so make sure we
849 // at least execute our cleanup code before
851 // prevent the window from being destroyed when the corresponding wxTLW is
852 // destroyed: this will result in a leak of a HWND, of course, but who
853 // cares when the process is being killed anyhow
854 if ( !wxTopLevelWindows
.empty() )
855 wxTopLevelWindows
[0]->SetHWND(0);
857 const int rc
= OnExit();
861 // calling exit() instead of ExitProcess() or not doing anything at all and
862 // being killed by Windows has the advantage of executing the dtors of
867 // Default behaviour: close the application with prompts. The
868 // user can veto the close, and therefore the end session.
869 void wxApp::OnQueryEndSession(wxCloseEvent
& event
)
873 if (!GetTopWindow()->Close(!event
.CanVeto()))
878 // ----------------------------------------------------------------------------
879 // system DLL versions
880 // ----------------------------------------------------------------------------
882 // these functions have trivial inline implementations for CE
885 #if wxUSE_DYNLIB_CLASS
890 // helper function: retrieve the DLL version by using DllGetVersion(), returns
891 // 0 if the DLL doesn't export such function
892 int CallDllGetVersion(wxDynamicLibrary
& dll
)
894 // now check if the function is available during run-time
895 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC
, DllGetVersion
, dll
);
896 if ( !pfnDllGetVersion
)
900 dvi
.cbSize
= sizeof(dvi
);
902 HRESULT hr
= (*pfnDllGetVersion
)(&dvi
);
905 wxLogApiError(wxT("DllGetVersion"), hr
);
910 return 100*dvi
.dwMajorVersion
+ dvi
.dwMinorVersion
;
913 } // anonymous namespace
916 int wxApp::GetComCtl32Version()
920 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
921 // but as its value should be the same both times it doesn't matter
922 static int s_verComCtl32
= -1;
924 if ( s_verComCtl32
== -1 )
926 // we're prepared to handle the errors
929 // we don't want to load comctl32.dll, it should be already loaded but,
930 // depending on the OS version and the presence of the manifest, it can
931 // be either v5 or v6 and instead of trying to guess it just get the
932 // handle of the already loaded version
933 wxLoadedDLL
dllComCtl32(wxT("comctl32.dll"));
934 if ( !dllComCtl32
.IsLoaded() )
940 // try DllGetVersion() for recent DLLs
941 s_verComCtl32
= CallDllGetVersion(dllComCtl32
);
943 // if DllGetVersion() is unavailable either during compile or
944 // run-time, try to guess the version otherwise
945 if ( !s_verComCtl32
)
947 // InitCommonControlsEx is unique to 4.70 and later
948 void *pfn
= dllComCtl32
.GetSymbol(wxT("InitCommonControlsEx"));
951 // not found, must be 4.00
956 // many symbols appeared in comctl32 4.71, could use any of
957 // them except may be DllInstall()
958 pfn
= dllComCtl32
.GetSymbol(wxT("InitializeFlatSB"));
961 // not found, must be 4.70
966 // found, must be 4.71 or later
973 return s_verComCtl32
;
977 int wxApp::GetShell32Version()
979 static int s_verShell32
= -1;
980 if ( s_verShell32
== -1 )
982 // we're prepared to handle the errors
985 wxDynamicLibrary
dllShell32(wxT("shell32.dll"), wxDL_VERBATIM
);
986 if ( dllShell32
.IsLoaded() )
988 s_verShell32
= CallDllGetVersion(dllShell32
);
992 // there doesn't seem to be any way to distinguish between 4.00
993 // and 4.70 (starting from 4.71 we have DllGetVersion()) so
994 // just assume it is 4.0
998 else // failed load the DLL?
1004 return s_verShell32
;
1007 #else // !wxUSE_DYNLIB_CLASS
1010 int wxApp::GetComCtl32Version()
1016 int wxApp::GetShell32Version()
1021 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
1023 #endif // !__WXWINCE__
1025 #if wxUSE_EXCEPTIONS
1027 // ----------------------------------------------------------------------------
1028 // exception handling
1029 // ----------------------------------------------------------------------------
1031 bool wxApp::OnExceptionInMainLoop()
1033 // ask the user about what to do: use the Win32 API function here as it
1034 // could be dangerous to use any wxWidgets code in this state
1039 wxT("An unhandled exception occurred. Press \"Abort\" to \
1040 terminate the program,\r\n\
1041 \"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
1042 wxT("Unhandled exception"),
1043 MB_ABORTRETRYIGNORE
|
1053 wxFAIL_MSG( wxT("unexpected MessageBox() return code") );
1064 #endif // wxUSE_EXCEPTIONS