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
)
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 ( !wxIsMainThread() || !wxEventLoop::GetActive() )
244 return DoSimpleWaitForThread(hThread
);
246 return ::MsgWaitForMultipleObjects
248 1, // number of objects to wait for
249 (HANDLE
*)&hThread
, // the objects
250 false, // wait for any objects, not all
251 INFINITE
, // no timeout
252 QS_ALLINPUT
| // return as soon as there are any events
257 wxPortId
wxGUIAppTraits::GetToolkitVersion(int *majVer
, int *minVer
) const
262 // on Windows, the toolkit version is the same of the OS version
263 // as Windows integrates the OS kernel with the GUI toolkit.
264 info
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
265 if ( ::GetVersionEx(&info
) )
268 *majVer
= info
.dwMajorVersion
;
270 *minVer
= info
.dwMinorVersion
;
273 #if defined(__WXHANDHELD__) || defined(__WXWINCE__)
282 wxTimerImpl
*wxGUIAppTraits::CreateTimerImpl(wxTimer
*timer
)
284 return new wxMSWTimerImpl(timer
);
287 #endif // wxUSE_TIMER
289 wxEventLoopBase
* wxGUIAppTraits::CreateEventLoop()
291 return new wxEventLoop
;
294 // ---------------------------------------------------------------------------
295 // Stuff for using console from the GUI applications
296 // ---------------------------------------------------------------------------
300 #if wxUSE_DYNLIB_CLASS
302 #include <wx/dynlib.h>
308 Helper class to manipulate console from a GUI app.
310 Notice that console output is available in the GUI app only if:
311 - AttachConsole() returns TRUE (which means it never works under pre-XP)
312 - we have a valid STD_ERROR_HANDLE
313 - command history hasn't been changed since our startup
315 To check if all these conditions are verified, you need to simple call
316 IsOkToUse(). It will check the first two conditions above the first time it
317 is called (and if this fails, the subsequent calls will return immediately)
318 and also recheck the last one every time it is called.
320 class wxConsoleStderr
323 // default ctor does nothing, call Init() before using this class
326 m_hStderr
= INVALID_HANDLE_VALUE
;
336 if ( m_hStderr
!= INVALID_HANDLE_VALUE
)
338 if ( !::FreeConsole() )
340 wxLogLastError(wxT("FreeConsole"));
345 // return true if we were successfully initialized and there had been no
346 // console activity which would interfere with our output since then
347 bool IsOkToUse() const
351 wxConsoleStderr
* const self
= const_cast<wxConsoleStderr
*>(this);
352 self
->m_ok
= self
->DoInit();
354 // no need to call IsHistoryUnchanged() as we just initialized
359 return m_ok
&& IsHistoryUnchanged();
363 // output the provided text on the console, return true if ok
364 bool Write(const wxString
& text
);
367 // called by Init() once only to do the real initialization
370 // retrieve the command line history into the provided buffer and return
372 int GetCommandHistory(wxWxCharBuffer
& buf
) const;
374 // check if the console history has changed
375 bool IsHistoryUnchanged() const;
377 int m_ok
; // initially -1, set to true or false by Init()
379 wxDynamicLibrary m_dllKernel32
;
381 HANDLE m_hStderr
; // console handle, if it's valid we must call
382 // FreeConsole() (even if m_ok != 1)
384 wxWxCharBuffer m_history
; // command history on startup
385 int m_historyLen
; // length command history buffer
387 wxCharBuffer m_data
; // data between empty line and cursor position
388 int m_dataLen
; // length data buffer
389 int m_dataLine
; // line offset
391 typedef DWORD (WINAPI
*GetConsoleCommandHistory_t
)(LPTSTR sCommands
,
394 typedef DWORD (WINAPI
*GetConsoleCommandHistoryLength_t
)(LPCTSTR sExeName
);
396 GetConsoleCommandHistory_t m_pfnGetConsoleCommandHistory
;
397 GetConsoleCommandHistoryLength_t m_pfnGetConsoleCommandHistoryLength
;
399 wxDECLARE_NO_COPY_CLASS(wxConsoleStderr
);
402 bool wxConsoleStderr::DoInit()
404 HANDLE hStderr
= ::GetStdHandle(STD_ERROR_HANDLE
);
406 if ( hStderr
== INVALID_HANDLE_VALUE
|| !hStderr
)
409 if ( !m_dllKernel32
.Load(wxT("kernel32.dll")) )
412 typedef BOOL (WINAPI
*AttachConsole_t
)(DWORD dwProcessId
);
413 AttachConsole_t
wxDL_INIT_FUNC(pfn
, AttachConsole
, m_dllKernel32
);
415 if ( !pfnAttachConsole
|| !pfnAttachConsole(ATTACH_PARENT_PROCESS
) )
418 // console attached, set m_hStderr now to ensure that we free it in the
422 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistory
, m_dllKernel32
);
423 if ( !m_pfnGetConsoleCommandHistory
)
426 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistoryLength
, m_dllKernel32
);
427 if ( !m_pfnGetConsoleCommandHistoryLength
)
430 // remember the current command history to be able to compare with it later
431 // in IsHistoryUnchanged()
432 m_historyLen
= GetCommandHistory(m_history
);
437 // now find the first blank line above the current position
438 CONSOLE_SCREEN_BUFFER_INFO csbi
;
440 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
442 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
448 pos
.Y
= csbi
.dwCursorPosition
.Y
+ 1;
450 // we decide that a line is empty if first 4 characters are spaces
456 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, buf
, WXSIZEOF(buf
),
459 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
462 } while ( wxStrncmp(" ", buf
, WXSIZEOF(buf
)) != 0 );
464 // calculate line offset and length of data
465 m_dataLine
= csbi
.dwCursorPosition
.Y
- pos
.Y
;
466 m_dataLen
= m_dataLine
*csbi
.dwMaximumWindowSize
.X
+ csbi
.dwCursorPosition
.X
;
470 m_data
.extend(m_dataLen
);
471 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, m_data
.data(), m_dataLen
,
474 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
482 int wxConsoleStderr::GetCommandHistory(wxWxCharBuffer
& buf
) const
484 // these functions are internal and may only be called by cmd.exe
485 static const wxChar
*CMD_EXE
= wxT("cmd.exe");
487 const int len
= m_pfnGetConsoleCommandHistoryLength(CMD_EXE
);
492 int len2
= m_pfnGetConsoleCommandHistory(buf
.data(), len
, CMD_EXE
);
495 // there seems to be a bug in the GetConsoleCommandHistoryA(), it
496 // returns the length of Unicode string and not ANSI one
498 #endif // !wxUSE_UNICODE
502 wxFAIL_MSG( wxT("failed getting history?") );
509 bool wxConsoleStderr::IsHistoryUnchanged() const
511 wxASSERT_MSG( m_ok
== 1, wxT("shouldn't be called if not initialized") );
513 // get (possibly changed) command history
514 wxWxCharBuffer history
;
515 const int historyLen
= GetCommandHistory(history
);
517 // and compare it with the original one
518 return historyLen
== m_historyLen
&& history
&&
519 memcmp(m_history
, history
, historyLen
) == 0;
522 bool wxConsoleStderr::Write(const wxString
& text
)
524 wxASSERT_MSG( m_hStderr
!= INVALID_HANDLE_VALUE
,
525 wxT("should only be called if Init() returned true") );
527 // get current position
528 CONSOLE_SCREEN_BUFFER_INFO csbi
;
529 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
531 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
535 // and calculate new position (where is empty line)
536 csbi
.dwCursorPosition
.X
= 0;
537 csbi
.dwCursorPosition
.Y
-= m_dataLine
;
539 if ( !::SetConsoleCursorPosition(m_hStderr
, csbi
.dwCursorPosition
) )
541 wxLogLastError(wxT("SetConsoleCursorPosition"));
546 if ( !::FillConsoleOutputCharacter(m_hStderr
, wxT(' '), m_dataLen
,
547 csbi
.dwCursorPosition
, &ret
) )
549 wxLogLastError(wxT("FillConsoleOutputCharacter"));
553 if ( !::WriteConsole(m_hStderr
, text
.wx_str(), text
.length(), &ret
, NULL
) )
555 wxLogLastError(wxT("WriteConsole"));
559 WriteConsoleA(m_hStderr
, m_data
, m_dataLen
, &ret
, 0);
564 wxConsoleStderr s_consoleStderr
;
566 } // anonymous namespace
568 bool wxGUIAppTraits::CanUseStderr()
570 return s_consoleStderr
.IsOkToUse();
573 bool wxGUIAppTraits::WriteToStderr(const wxString
& text
)
575 return s_consoleStderr
.IsOkToUse() && s_consoleStderr
.Write(text
);
578 #else // !wxUSE_DYNLIB_CLASS
580 bool wxGUIAppTraits::CanUseStderr()
585 bool wxGUIAppTraits::WriteToStderr(const wxString
& WXUNUSED(text
))
590 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
592 #endif // !__WXWINCE__
594 // ===========================================================================
595 // wxApp implementation
596 // ===========================================================================
598 int wxApp::m_nCmdShow
= SW_SHOWNORMAL
;
600 // ---------------------------------------------------------------------------
602 // ---------------------------------------------------------------------------
604 IMPLEMENT_DYNAMIC_CLASS(wxApp
, wxEvtHandler
)
606 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
607 EVT_IDLE(wxApp::OnIdle
)
608 EVT_END_SESSION(wxApp::OnEndSession
)
609 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession
)
612 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
614 class wxCallBaseCleanup
617 wxCallBaseCleanup(wxApp
*app
) : m_app(app
) { }
618 ~wxCallBaseCleanup() { if ( m_app
) m_app
->wxAppBase::CleanUp(); }
620 void Dismiss() { m_app
= NULL
; }
627 bool wxApp::Initialize(int& argc
, wxChar
**argv
)
629 if ( !wxAppBase::Initialize(argc
, argv
) )
632 // ensure that base cleanup is done if we return too early
633 wxCallBaseCleanup
callBaseCleanup(this);
635 #if !defined(__WXMICROWIN__)
636 InitCommonControls();
637 #endif // !defined(__WXMICROWIN__)
639 #if defined(__SMARTPHONE__) || defined(__POCKETPC__)
640 SHInitExtraControls();
644 // Don't show a message box if a function such as SHGetFileInfo
645 // fails to find a device.
646 SetErrorMode(SEM_FAILCRITICALERRORS
|SEM_NOOPENFILEERRORBOX
);
651 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
652 wxSetKeyboardHook(true);
655 callBaseCleanup
.Dismiss();
660 // ---------------------------------------------------------------------------
661 // Win32 window class registration
662 // ---------------------------------------------------------------------------
665 const wxChar
*wxApp::GetRegisteredClassName(const wxChar
*name
,
669 const size_t count
= gs_regClassesInfo
.size();
670 for ( size_t n
= 0; n
< count
; n
++ )
672 if ( gs_regClassesInfo
[n
].regname
== name
)
673 return gs_regClassesInfo
[n
].regname
.c_str();
676 // we need to register this class
678 wxZeroMemory(wndclass
);
680 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
681 wndclass
.hInstance
= wxGetInstance();
682 wndclass
.hCursor
= ::LoadCursor(NULL
, IDC_ARROW
);
683 wndclass
.hbrBackground
= (HBRUSH
)wxUIntToPtr(bgBrushCol
+ 1);
684 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| extraStyles
;
687 ClassRegInfo
regClass(name
);
688 wndclass
.lpszClassName
= regClass
.regname
.wx_str();
689 if ( !::RegisterClass(&wndclass
) )
691 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
696 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
697 wndclass
.lpszClassName
= regClass
.regnameNR
.wx_str();
698 if ( !::RegisterClass(&wndclass
) )
700 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
702 ::UnregisterClass(regClass
.regname
.c_str(), wxGetInstance());
706 gs_regClassesInfo
.push_back(regClass
);
708 // take care to return the pointer which will remain valid after the
709 // function returns (it could be invalidated later if new elements are
710 // added to the vector and it's reallocated but this shouldn't matter as
711 // this pointer should be used right now, not stored)
712 return gs_regClassesInfo
.back().regname
.wx_str();
715 bool wxApp::IsRegisteredClassName(const wxString
& name
)
717 const size_t count
= gs_regClassesInfo
.size();
718 for ( size_t n
= 0; n
< count
; n
++ )
720 if ( gs_regClassesInfo
[n
].regname
== name
||
721 gs_regClassesInfo
[n
].regnameNR
== name
)
728 void wxApp::UnregisterWindowClasses()
730 const size_t count
= gs_regClassesInfo
.size();
731 for ( size_t n
= 0; n
< count
; n
++ )
733 const ClassRegInfo
& regClass
= gs_regClassesInfo
[n
];
734 if ( !::UnregisterClass(regClass
.regname
.c_str(), wxGetInstance()) )
736 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
740 if ( !::UnregisterClass(regClass
.regnameNR
.c_str(), wxGetInstance()) )
742 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
743 regClass
.regnameNR
));
747 gs_regClassesInfo
.clear();
750 void wxApp::CleanUp()
752 // all objects pending for deletion must be deleted first, otherwise
753 // UnregisterWindowClasses() call wouldn't succeed (because windows
754 // using the classes being unregistered still exist), so call the base
755 // class method first and only then do our clean up
756 wxAppBase::CleanUp();
758 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
759 wxSetKeyboardHook(false);
764 // for an EXE the classes are unregistered when it terminates but DLL may
765 // be loaded several times (load/unload/load) into the same process in
766 // which case the registration will fail after the first time if we don't
767 // unregister the classes now
768 UnregisterWindowClasses();
771 // ----------------------------------------------------------------------------
773 // ----------------------------------------------------------------------------
777 m_printMode
= wxPRINT_WINDOWS
;
784 // ----------------------------------------------------------------------------
785 // wxApp idle handling
786 // ----------------------------------------------------------------------------
788 void wxApp::OnIdle(wxIdleEvent
& WXUNUSED(event
))
790 #if wxUSE_DC_CACHEING
791 // automated DC cache management: clear the cached DCs and bitmap
792 // if it's likely that the app has finished with them, that is, we
793 // get an idle event and we're not dragging anything.
794 if (!::GetKeyState(MK_LBUTTON
) && !::GetKeyState(MK_MBUTTON
) && !::GetKeyState(MK_RBUTTON
))
795 wxMSWDCImpl::ClearCache();
796 #endif // wxUSE_DC_CACHEING
799 void wxApp::WakeUpIdle()
801 // Send the top window a dummy message so idle handler processing will
802 // start up again. Doing it this way ensures that the idle handler
803 // wakes up in the right thread (see also wxWakeUpMainThread() which does
804 // the same for the main app thread only)
805 wxWindow
* const topWindow
= wxTheApp
->GetTopWindow();
808 HWND hwndTop
= GetHwndOf(topWindow
);
810 // Do not post WM_NULL if there's already a pending WM_NULL to avoid
811 // overflowing the message queue.
813 // Notice that due to a limitation of PeekMessage() API (which handles
814 // 0,0 range specially), we have to check the range from 0-1 instead.
815 // This still makes it possible to overflow the queue with WM_NULLs by
816 // interspersing the calles to WakeUpIdle() with windows creation but
817 // it should be rather hard to do it accidentally.
819 if ( !::PeekMessage(&msg
, hwndTop
, 0, 1, PM_NOREMOVE
) ||
820 ::PeekMessage(&msg
, hwndTop
, 1, 1, PM_NOREMOVE
) )
822 if ( !::PostMessage(hwndTop
, WM_NULL
, 0, 0) )
824 // should never happen
825 wxLogLastError(wxT("PostMessage(WM_NULL)"));
831 wxWakeUpMainThread();
832 #endif // wxUSE_THREADS
835 // ----------------------------------------------------------------------------
836 // other wxApp event hanlders
837 // ----------------------------------------------------------------------------
839 void wxApp::OnEndSession(wxCloseEvent
& WXUNUSED(event
))
841 // Windows will terminate the process soon after we return from
842 // WM_ENDSESSION handler or when we delete our last window, so make sure we
843 // at least execute our cleanup code before
845 // prevent the window from being destroyed when the corresponding wxTLW is
846 // destroyed: this will result in a leak of a HWND, of course, but who
847 // cares when the process is being killed anyhow
848 if ( !wxTopLevelWindows
.empty() )
849 wxTopLevelWindows
[0]->SetHWND(0);
851 const int rc
= OnExit();
855 // calling exit() instead of ExitProcess() or not doing anything at all and
856 // being killed by Windows has the advantage of executing the dtors of
861 // Default behaviour: close the application with prompts. The
862 // user can veto the close, and therefore the end session.
863 void wxApp::OnQueryEndSession(wxCloseEvent
& event
)
867 if (!GetTopWindow()->Close(!event
.CanVeto()))
872 // ----------------------------------------------------------------------------
873 // system DLL versions
874 // ----------------------------------------------------------------------------
876 // these functions have trivial inline implementations for CE
879 #if wxUSE_DYNLIB_CLASS
884 // helper function: retrieve the DLL version by using DllGetVersion(), returns
885 // 0 if the DLL doesn't export such function
886 int CallDllGetVersion(wxDynamicLibrary
& dll
)
888 // now check if the function is available during run-time
889 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC
, DllGetVersion
, dll
);
890 if ( !pfnDllGetVersion
)
894 dvi
.cbSize
= sizeof(dvi
);
896 HRESULT hr
= (*pfnDllGetVersion
)(&dvi
);
899 wxLogApiError(wxT("DllGetVersion"), hr
);
904 return 100*dvi
.dwMajorVersion
+ dvi
.dwMinorVersion
;
907 } // anonymous namespace
910 int wxApp::GetComCtl32Version()
914 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
915 // but as its value should be the same both times it doesn't matter
916 static int s_verComCtl32
= -1;
918 if ( s_verComCtl32
== -1 )
920 // we're prepared to handle the errors
923 // we don't want to load comctl32.dll, it should be already loaded but,
924 // depending on the OS version and the presence of the manifest, it can
925 // be either v5 or v6 and instead of trying to guess it just get the
926 // handle of the already loaded version
927 wxLoadedDLL
dllComCtl32(wxT("comctl32.dll"));
928 if ( !dllComCtl32
.IsLoaded() )
934 // try DllGetVersion() for recent DLLs
935 s_verComCtl32
= CallDllGetVersion(dllComCtl32
);
937 // if DllGetVersion() is unavailable either during compile or
938 // run-time, try to guess the version otherwise
939 if ( !s_verComCtl32
)
941 // InitCommonControlsEx is unique to 4.70 and later
942 void *pfn
= dllComCtl32
.GetSymbol(wxT("InitCommonControlsEx"));
945 // not found, must be 4.00
950 // many symbols appeared in comctl32 4.71, could use any of
951 // them except may be DllInstall()
952 pfn
= dllComCtl32
.GetSymbol(wxT("InitializeFlatSB"));
955 // not found, must be 4.70
960 // found, must be 4.71 or later
967 return s_verComCtl32
;
971 int wxApp::GetShell32Version()
973 static int s_verShell32
= -1;
974 if ( s_verShell32
== -1 )
976 // we're prepared to handle the errors
979 wxDynamicLibrary
dllShell32(wxT("shell32.dll"), wxDL_VERBATIM
);
980 if ( dllShell32
.IsLoaded() )
982 s_verShell32
= CallDllGetVersion(dllShell32
);
986 // there doesn't seem to be any way to distinguish between 4.00
987 // and 4.70 (starting from 4.71 we have DllGetVersion()) so
988 // just assume it is 4.0
992 else // failed load the DLL?
1001 #else // !wxUSE_DYNLIB_CLASS
1004 int wxApp::GetComCtl32Version()
1010 int wxApp::GetShell32Version()
1015 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
1017 #endif // !__WXWINCE__
1019 #if wxUSE_EXCEPTIONS
1021 // ----------------------------------------------------------------------------
1022 // exception handling
1023 // ----------------------------------------------------------------------------
1025 bool wxApp::OnExceptionInMainLoop()
1027 // ask the user about what to do: use the Win32 API function here as it
1028 // could be dangerous to use any wxWidgets code in this state
1033 wxT("An unhandled exception occurred. Press \"Abort\" to \
1034 terminate the program,\r\n\
1035 \"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
1036 wxT("Unhandled exception"),
1037 MB_ABORTRETRYIGNORE
|
1047 wxFAIL_MSG( wxT("unexpected MessageBox() return code") );
1058 #endif // wxUSE_EXCEPTIONS