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
131 const wxString regname
;
132 const wxString regnameNR
;
134 wxDECLARE_NO_ASSIGN_CLASS(ClassRegInfo
);
140 wxVector
<ClassRegInfo
> gs_regClassesInfo
;
142 } // anonymous namespace
144 // ----------------------------------------------------------------------------
146 // ----------------------------------------------------------------------------
148 LRESULT WXDLLEXPORT APIENTRY
wxWndProc(HWND
, UINT
, WPARAM
, LPARAM
);
150 // ===========================================================================
151 // wxGUIAppTraits implementation
152 // ===========================================================================
154 // private class which we use to pass parameters from BeforeChildWaitLoop() to
155 // AfterChildWaitLoop()
156 struct ChildWaitLoopData
158 ChildWaitLoopData(wxWindowDisabler
*wd_
, wxWindow
*winActive_
)
161 winActive
= winActive_
;
164 wxWindowDisabler
*wd
;
168 void *wxGUIAppTraits::BeforeChildWaitLoop()
171 We use a dirty hack here to disable all application windows (which we
172 must do because otherwise the calls to wxYield() could lead to some very
173 unexpected reentrancies in the users code) but to avoid losing
174 focus/activation entirely when the child process terminates which would
175 happen if we simply disabled everything using wxWindowDisabler. Indeed,
176 remember that Windows will never activate a disabled window and when the
177 last childs window is closed and Windows looks for a window to activate
178 all our windows are still disabled. There is no way to enable them in
179 time because we don't know when the childs windows are going to be
180 closed, so the solution we use here is to keep one special tiny frame
181 enabled all the time. Then when the child terminates it will get
182 activated and when we close it below -- after reenabling all the other
183 windows! -- the previously active window becomes activated again and
188 // first disable all existing windows
189 wxWindowDisabler
*wd
= new wxWindowDisabler
;
191 // then create an "invisible" frame: it has minimal size, is positioned
192 // (hopefully) outside the screen and doesn't appear on the taskbar
193 wxWindow
*winActive
= new wxFrame
195 wxTheApp
->GetTopWindow(),
198 wxPoint(32600, 32600),
200 wxDEFAULT_FRAME_STYLE
| wxFRAME_NO_TASKBAR
204 return new ChildWaitLoopData(wd
, winActive
);
207 void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig
)
211 ChildWaitLoopData
* const data
= (ChildWaitLoopData
*)dataOrig
;
215 // finally delete the dummy frame and, as wd has been already destroyed and
216 // the other windows reenabled, the activation is going to return to the
217 // window which had had it before
218 data
->winActive
->Destroy();
220 // also delete the temporary data object itself
224 bool wxGUIAppTraits::DoMessageFromThreadWait()
226 // we should return false only if the app should exit, i.e. only if
227 // Dispatch() determines that the main event loop should terminate
228 wxEventLoopBase
* const evtLoop
= wxEventLoop::GetActive();
229 if ( !evtLoop
|| !evtLoop
->Pending() )
231 // no events means no quit event
235 return evtLoop
->Dispatch();
238 DWORD
wxGUIAppTraits::WaitForThread(WXHANDLE hThread
)
240 // if we don't have a running event loop, we shouldn't wait for the
241 // messages as we never remove them from the message queue and so we enter
242 // an infinite loop as MsgWaitForMultipleObjects() keeps returning
244 if ( !wxEventLoop::GetActive() )
245 return DoSimpleWaitForThread(hThread
);
247 return ::MsgWaitForMultipleObjects
249 1, // number of objects to wait for
250 (HANDLE
*)&hThread
, // the objects
251 false, // wait for any objects, not all
252 INFINITE
, // no timeout
253 QS_ALLINPUT
| // return as soon as there are any events
258 wxPortId
wxGUIAppTraits::GetToolkitVersion(int *majVer
, int *minVer
) const
263 // on Windows, the toolkit version is the same of the OS version
264 // as Windows integrates the OS kernel with the GUI toolkit.
265 info
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
266 if ( ::GetVersionEx(&info
) )
269 *majVer
= info
.dwMajorVersion
;
271 *minVer
= info
.dwMinorVersion
;
274 #if defined(__WXHANDHELD__) || defined(__WXWINCE__)
283 wxTimerImpl
*wxGUIAppTraits::CreateTimerImpl(wxTimer
*timer
)
285 return new wxMSWTimerImpl(timer
);
288 #endif // wxUSE_TIMER
290 wxEventLoopBase
* wxGUIAppTraits::CreateEventLoop()
292 return new wxEventLoop
;
295 // ---------------------------------------------------------------------------
296 // Stuff for using console from the GUI applications
297 // ---------------------------------------------------------------------------
301 #include <wx/dynlib.h>
307 Helper class to manipulate console from a GUI app.
309 Notice that console output is available in the GUI app only if:
310 - AttachConsole() returns TRUE (which means it never works under pre-XP)
311 - we have a valid STD_ERROR_HANDLE
312 - command history hasn't been changed since our startup
314 To check if all these conditions are verified, you need to simple call
315 IsOkToUse(). It will check the first two conditions above the first time it
316 is called (and if this fails, the subsequent calls will return immediately)
317 and also recheck the last one every time it is called.
319 class wxConsoleStderr
322 // default ctor does nothing, call Init() before using this class
325 m_hStderr
= INVALID_HANDLE_VALUE
;
335 if ( m_hStderr
!= INVALID_HANDLE_VALUE
)
337 if ( !::FreeConsole() )
339 wxLogLastError(wxT("FreeConsole"));
344 // return true if we were successfully initialized and there had been no
345 // console activity which would interfere with our output since then
346 bool IsOkToUse() const
350 wxConsoleStderr
* const self
= const_cast<wxConsoleStderr
*>(this);
351 self
->m_ok
= self
->DoInit();
353 // no need to call IsHistoryUnchanged() as we just initialized
358 return m_ok
&& IsHistoryUnchanged();
362 // output the provided text on the console, return true if ok
363 bool Write(const wxString
& text
);
366 // called by Init() once only to do the real initialization
369 // retrieve the command line history into the provided buffer and return
371 int GetCommandHistory(wxWxCharBuffer
& buf
) const;
373 // check if the console history has changed
374 bool IsHistoryUnchanged() const;
376 int m_ok
; // initially -1, set to true or false by Init()
378 wxDynamicLibrary m_dllKernel32
;
380 HANDLE m_hStderr
; // console handle, if it's valid we must call
381 // FreeConsole() (even if m_ok != 1)
383 wxWxCharBuffer m_history
; // command history on startup
384 int m_historyLen
; // length command history buffer
386 wxCharBuffer m_data
; // data between empty line and cursor position
387 int m_dataLen
; // length data buffer
388 int m_dataLine
; // line offset
390 typedef DWORD (WINAPI
*GetConsoleCommandHistory_t
)(LPTSTR sCommands
,
393 typedef DWORD (WINAPI
*GetConsoleCommandHistoryLength_t
)(LPCTSTR sExeName
);
395 GetConsoleCommandHistory_t m_pfnGetConsoleCommandHistory
;
396 GetConsoleCommandHistoryLength_t m_pfnGetConsoleCommandHistoryLength
;
398 wxDECLARE_NO_COPY_CLASS(wxConsoleStderr
);
401 bool wxConsoleStderr::DoInit()
403 HANDLE hStderr
= ::GetStdHandle(STD_ERROR_HANDLE
);
405 if ( hStderr
== INVALID_HANDLE_VALUE
|| !hStderr
)
408 if ( !m_dllKernel32
.Load(wxT("kernel32.dll")) )
411 typedef BOOL (WINAPI
*AttachConsole_t
)(DWORD dwProcessId
);
412 AttachConsole_t
wxDL_INIT_FUNC(pfn
, AttachConsole
, m_dllKernel32
);
414 if ( !pfnAttachConsole
|| !pfnAttachConsole(ATTACH_PARENT_PROCESS
) )
417 // console attached, set m_hStderr now to ensure that we free it in the
421 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistory
, m_dllKernel32
);
422 if ( !m_pfnGetConsoleCommandHistory
)
425 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistoryLength
, m_dllKernel32
);
426 if ( !m_pfnGetConsoleCommandHistoryLength
)
429 // remember the current command history to be able to compare with it later
430 // in IsHistoryUnchanged()
431 m_historyLen
= GetCommandHistory(m_history
);
436 // now find the first blank line above the current position
437 CONSOLE_SCREEN_BUFFER_INFO csbi
;
439 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
441 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
447 pos
.Y
= csbi
.dwCursorPosition
.Y
+ 1;
449 // we decide that a line is empty if first 4 characters are spaces
455 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, buf
, WXSIZEOF(buf
),
458 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
461 } while ( wxStrncmp(" ", buf
, WXSIZEOF(buf
)) != 0 );
463 // calculate line offset and length of data
464 m_dataLine
= csbi
.dwCursorPosition
.Y
- pos
.Y
;
465 m_dataLen
= m_dataLine
*csbi
.dwMaximumWindowSize
.X
+ csbi
.dwCursorPosition
.X
;
469 m_data
.extend(m_dataLen
);
470 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, m_data
.data(), m_dataLen
,
473 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
481 int wxConsoleStderr::GetCommandHistory(wxWxCharBuffer
& buf
) const
483 // these functions are internal and may only be called by cmd.exe
484 static const wxChar
*CMD_EXE
= wxT("cmd.exe");
486 const int len
= m_pfnGetConsoleCommandHistoryLength(CMD_EXE
);
491 int len2
= m_pfnGetConsoleCommandHistory(buf
.data(), len
, CMD_EXE
);
494 // there seems to be a bug in the GetConsoleCommandHistoryA(), it
495 // returns the length of Unicode string and not ANSI one
497 #endif // !wxUSE_UNICODE
501 wxFAIL_MSG( wxT("failed getting history?") );
508 bool wxConsoleStderr::IsHistoryUnchanged() const
510 wxASSERT_MSG( m_ok
== 1, wxT("shouldn't be called if not initialized") );
512 // get (possibly changed) command history
513 wxWxCharBuffer history
;
514 const int historyLen
= GetCommandHistory(history
);
516 // and compare it with the original one
517 return historyLen
== m_historyLen
&& history
&&
518 memcmp(m_history
, history
, historyLen
) == 0;
521 bool wxConsoleStderr::Write(const wxString
& text
)
523 wxASSERT_MSG( m_hStderr
!= INVALID_HANDLE_VALUE
,
524 wxT("should only be called if Init() returned true") );
526 // get current position
527 CONSOLE_SCREEN_BUFFER_INFO csbi
;
528 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
530 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
534 // and calculate new position (where is empty line)
535 csbi
.dwCursorPosition
.X
= 0;
536 csbi
.dwCursorPosition
.Y
-= m_dataLine
;
538 if ( !::SetConsoleCursorPosition(m_hStderr
, csbi
.dwCursorPosition
) )
540 wxLogLastError(wxT("SetConsoleCursorPosition"));
545 if ( !::FillConsoleOutputCharacter(m_hStderr
, wxT(' '), m_dataLen
,
546 csbi
.dwCursorPosition
, &ret
) )
548 wxLogLastError(wxT("FillConsoleOutputCharacter"));
552 if ( !::WriteConsole(m_hStderr
, text
.wx_str(), text
.length(), &ret
, NULL
) )
554 wxLogLastError(wxT("WriteConsole"));
558 WriteConsoleA(m_hStderr
, m_data
, m_dataLen
, &ret
, 0);
563 wxConsoleStderr s_consoleStderr
;
565 } // anonymous namespace
567 bool wxGUIAppTraits::CanUseStderr()
569 return s_consoleStderr
.IsOkToUse();
572 bool wxGUIAppTraits::WriteToStderr(const wxString
& text
)
574 return s_consoleStderr
.IsOkToUse() && s_consoleStderr
.Write(text
);
577 #endif // !__WXWINCE__
579 // ===========================================================================
580 // wxApp implementation
581 // ===========================================================================
583 int wxApp::m_nCmdShow
= SW_SHOWNORMAL
;
585 // ---------------------------------------------------------------------------
587 // ---------------------------------------------------------------------------
589 IMPLEMENT_DYNAMIC_CLASS(wxApp
, wxEvtHandler
)
591 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
592 EVT_IDLE(wxApp::OnIdle
)
593 EVT_END_SESSION(wxApp::OnEndSession
)
594 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession
)
597 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
599 class wxCallBaseCleanup
602 wxCallBaseCleanup(wxApp
*app
) : m_app(app
) { }
603 ~wxCallBaseCleanup() { if ( m_app
) m_app
->wxAppBase::CleanUp(); }
605 void Dismiss() { m_app
= NULL
; }
612 bool wxApp::Initialize(int& argc
, wxChar
**argv
)
614 if ( !wxAppBase::Initialize(argc
, argv
) )
617 // ensure that base cleanup is done if we return too early
618 wxCallBaseCleanup
callBaseCleanup(this);
620 #if !defined(__WXMICROWIN__)
621 InitCommonControls();
622 #endif // !defined(__WXMICROWIN__)
624 #if defined(__SMARTPHONE__) || defined(__POCKETPC__)
625 SHInitExtraControls();
629 // Don't show a message box if a function such as SHGetFileInfo
630 // fails to find a device.
631 SetErrorMode(SEM_FAILCRITICALERRORS
|SEM_NOOPENFILEERRORBOX
);
636 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
637 wxSetKeyboardHook(true);
640 callBaseCleanup
.Dismiss();
645 // ---------------------------------------------------------------------------
646 // Win32 window class registration
647 // ---------------------------------------------------------------------------
650 const wxChar
*wxApp::GetRegisteredClassName(const wxChar
*name
,
654 const size_t count
= gs_regClassesInfo
.size();
655 for ( size_t n
= 0; n
< count
; n
++ )
657 if ( gs_regClassesInfo
[n
].regname
== name
)
658 return gs_regClassesInfo
[n
].regname
.c_str();
661 // we need to register this class
663 wxZeroMemory(wndclass
);
665 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
666 wndclass
.hInstance
= wxGetInstance();
667 wndclass
.hCursor
= ::LoadCursor(NULL
, IDC_ARROW
);
668 wndclass
.hbrBackground
= (HBRUSH
)wxUIntToPtr(bgBrushCol
+ 1);
669 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| extraStyles
;
672 ClassRegInfo
regClass(name
);
673 wndclass
.lpszClassName
= regClass
.regname
.wx_str();
674 if ( !::RegisterClass(&wndclass
) )
676 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
681 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
682 wndclass
.lpszClassName
= regClass
.regnameNR
.wx_str();
683 if ( !::RegisterClass(&wndclass
) )
685 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
687 ::UnregisterClass(regClass
.regname
.c_str(), wxGetInstance());
691 gs_regClassesInfo
.push_back(regClass
);
693 // take care to return the pointer which will remain valid after the
694 // function returns (it could be invalidated later if new elements are
695 // added to the vector and it's reallocated but this shouldn't matter as
696 // this pointer should be used right now, not stored)
697 return gs_regClassesInfo
.back().regname
.wx_str();
700 bool wxApp::IsRegisteredClassName(const wxString
& name
)
702 const size_t count
= gs_regClassesInfo
.size();
703 for ( size_t n
= 0; n
< count
; n
++ )
705 if ( gs_regClassesInfo
[n
].regname
== name
||
706 gs_regClassesInfo
[n
].regnameNR
== name
)
713 void wxApp::UnregisterWindowClasses()
715 const size_t count
= gs_regClassesInfo
.size();
716 for ( size_t n
= 0; n
< count
; n
++ )
718 const ClassRegInfo
& regClass
= gs_regClassesInfo
[n
];
719 if ( !::UnregisterClass(regClass
.regname
.c_str(), wxGetInstance()) )
721 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
725 if ( !::UnregisterClass(regClass
.regnameNR
.c_str(), wxGetInstance()) )
727 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
728 regClass
.regnameNR
));
732 gs_regClassesInfo
.clear();
735 void wxApp::CleanUp()
737 // all objects pending for deletion must be deleted first, otherwise
738 // UnregisterWindowClasses() call wouldn't succeed (because windows
739 // using the classes being unregistered still exist), so call the base
740 // class method first and only then do our clean up
741 wxAppBase::CleanUp();
743 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
744 wxSetKeyboardHook(false);
749 // for an EXE the classes are unregistered when it terminates but DLL may
750 // be loaded several times (load/unload/load) into the same process in
751 // which case the registration will fail after the first time if we don't
752 // unregister the classes now
753 UnregisterWindowClasses();
756 // ----------------------------------------------------------------------------
758 // ----------------------------------------------------------------------------
762 m_printMode
= wxPRINT_WINDOWS
;
769 // ----------------------------------------------------------------------------
770 // wxApp idle handling
771 // ----------------------------------------------------------------------------
773 void wxApp::OnIdle(wxIdleEvent
& WXUNUSED(event
))
775 #if wxUSE_DC_CACHEING
776 // automated DC cache management: clear the cached DCs and bitmap
777 // if it's likely that the app has finished with them, that is, we
778 // get an idle event and we're not dragging anything.
779 if (!::GetKeyState(MK_LBUTTON
) && !::GetKeyState(MK_MBUTTON
) && !::GetKeyState(MK_RBUTTON
))
780 wxMSWDCImpl::ClearCache();
781 #endif // wxUSE_DC_CACHEING
784 void wxApp::WakeUpIdle()
786 // Send the top window a dummy message so idle handler processing will
787 // start up again. Doing it this way ensures that the idle handler
788 // wakes up in the right thread (see also wxWakeUpMainThread() which does
789 // the same for the main app thread only)
790 wxWindow
* const topWindow
= wxTheApp
->GetTopWindow();
793 HWND hwndTop
= GetHwndOf(topWindow
);
795 // Do not post WM_NULL if there's already a pending WM_NULL to avoid
796 // overflowing the message queue.
798 // Notice that due to a limitation of PeekMessage() API (which handles
799 // 0,0 range specially), we have to check the range from 0-1 instead.
800 // This still makes it possible to overflow the queue with WM_NULLs by
801 // interspersing the calles to WakeUpIdle() with windows creation but
802 // it should be rather hard to do it accidentally.
804 if ( !::PeekMessage(&msg
, hwndTop
, 0, 1, PM_NOREMOVE
) ||
805 ::PeekMessage(&msg
, hwndTop
, 1, 1, PM_NOREMOVE
) )
807 if ( !::PostMessage(hwndTop
, WM_NULL
, 0, 0) )
809 // should never happen
810 wxLogLastError(wxT("PostMessage(WM_NULL)"));
816 wxWakeUpMainThread();
817 #endif // wxUSE_THREADS
820 // ----------------------------------------------------------------------------
821 // other wxApp event hanlders
822 // ----------------------------------------------------------------------------
824 void wxApp::OnEndSession(wxCloseEvent
& WXUNUSED(event
))
826 // Windows will terminate the process soon after we return from
827 // WM_ENDSESSION handler or when we delete our last window, so make sure we
828 // at least execute our cleanup code before
830 // prevent the window from being destroyed when the corresponding wxTLW is
831 // destroyed: this will result in a leak of a HWND, of course, but who
832 // cares when the process is being killed anyhow
833 if ( !wxTopLevelWindows
.empty() )
834 wxTopLevelWindows
[0]->SetHWND(0);
836 const int rc
= OnExit();
840 // calling exit() instead of ExitProcess() or not doing anything at all and
841 // being killed by Windows has the advantage of executing the dtors of
846 // Default behaviour: close the application with prompts. The
847 // user can veto the close, and therefore the end session.
848 void wxApp::OnQueryEndSession(wxCloseEvent
& event
)
852 if (!GetTopWindow()->Close(!event
.CanVeto()))
857 // ----------------------------------------------------------------------------
858 // system DLL versions
859 // ----------------------------------------------------------------------------
861 // these functions have trivial inline implementations for CE
864 #if wxUSE_DYNLIB_CLASS
869 // helper function: retrieve the DLL version by using DllGetVersion(), returns
870 // 0 if the DLL doesn't export such function
871 int CallDllGetVersion(wxDynamicLibrary
& dll
)
873 // now check if the function is available during run-time
874 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC
, DllGetVersion
, dll
);
875 if ( !pfnDllGetVersion
)
879 dvi
.cbSize
= sizeof(dvi
);
881 HRESULT hr
= (*pfnDllGetVersion
)(&dvi
);
884 wxLogApiError(wxT("DllGetVersion"), hr
);
889 return 100*dvi
.dwMajorVersion
+ dvi
.dwMinorVersion
;
892 } // anonymous namespace
895 int wxApp::GetComCtl32Version()
899 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
900 // but as its value should be the same both times it doesn't matter
901 static int s_verComCtl32
= -1;
903 if ( s_verComCtl32
== -1 )
905 // we're prepared to handle the errors
908 // we don't want to load comctl32.dll, it should be already loaded but,
909 // depending on the OS version and the presence of the manifest, it can
910 // be either v5 or v6 and instead of trying to guess it just get the
911 // handle of the already loaded version
912 wxLoadedDLL
dllComCtl32(wxT("comctl32.dll"));
913 if ( !dllComCtl32
.IsLoaded() )
919 // try DllGetVersion() for recent DLLs
920 s_verComCtl32
= CallDllGetVersion(dllComCtl32
);
922 // if DllGetVersion() is unavailable either during compile or
923 // run-time, try to guess the version otherwise
924 if ( !s_verComCtl32
)
926 // InitCommonControlsEx is unique to 4.70 and later
927 void *pfn
= dllComCtl32
.GetSymbol(wxT("InitCommonControlsEx"));
930 // not found, must be 4.00
935 // many symbols appeared in comctl32 4.71, could use any of
936 // them except may be DllInstall()
937 pfn
= dllComCtl32
.GetSymbol(wxT("InitializeFlatSB"));
940 // not found, must be 4.70
945 // found, must be 4.71 or later
952 return s_verComCtl32
;
956 int wxApp::GetShell32Version()
958 static int s_verShell32
= -1;
959 if ( s_verShell32
== -1 )
961 // we're prepared to handle the errors
964 wxDynamicLibrary
dllShell32(wxT("shell32.dll"), wxDL_VERBATIM
);
965 if ( dllShell32
.IsLoaded() )
967 s_verShell32
= CallDllGetVersion(dllShell32
);
971 // there doesn't seem to be any way to distinguish between 4.00
972 // and 4.70 (starting from 4.71 we have DllGetVersion()) so
973 // just assume it is 4.0
977 else // failed load the DLL?
986 #else // !wxUSE_DYNLIB_CLASS
989 int wxApp::GetComCtl32Version()
995 int wxApp::GetShell32Version()
1000 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
1002 #endif // !__WXWINCE__
1004 #if wxUSE_EXCEPTIONS
1006 // ----------------------------------------------------------------------------
1007 // exception handling
1008 // ----------------------------------------------------------------------------
1010 bool wxApp::OnExceptionInMainLoop()
1012 // ask the user about what to do: use the Win32 API function here as it
1013 // could be dangerous to use any wxWidgets code in this state
1018 wxT("An unhandled exception occurred. Press \"Abort\" to \
1019 terminate the program,\r\n\
1020 \"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
1021 wxT("Unhandled exception"),
1022 MB_ABORTRETRYIGNORE
|
1032 wxFAIL_MSG( wxT("unexpected MessageBox() return code") );
1043 #endif // wxUSE_EXCEPTIONS