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 // if we don't have a running event loop, we shouldn't wait for the
239 // messages as we never remove them from the message queue and so we enter
240 // an infinite loop as MsgWaitForMultipleObjects() keeps returning
242 if ( !wxEventLoop::GetActive() )
243 return DoSimpleWaitForThread(hThread
);
245 return ::MsgWaitForMultipleObjects
247 1, // number of objects to wait for
248 (HANDLE
*)&hThread
, // the objects
249 false, // wait for any objects, not all
250 INFINITE
, // no timeout
251 QS_ALLINPUT
| // return as soon as there are any events
256 wxPortId
wxGUIAppTraits::GetToolkitVersion(int *majVer
, int *minVer
) const
261 // on Windows, the toolkit version is the same of the OS version
262 // as Windows integrates the OS kernel with the GUI toolkit.
263 info
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
264 if ( ::GetVersionEx(&info
) )
267 *majVer
= info
.dwMajorVersion
;
269 *minVer
= info
.dwMinorVersion
;
272 #if defined(__WXHANDHELD__) || defined(__WXWINCE__)
281 wxTimerImpl
*wxGUIAppTraits::CreateTimerImpl(wxTimer
*timer
)
283 return new wxMSWTimerImpl(timer
);
286 #endif // wxUSE_TIMER
288 wxEventLoopBase
* wxGUIAppTraits::CreateEventLoop()
290 return new wxEventLoop
;
293 // ---------------------------------------------------------------------------
294 // Stuff for using console from the GUI applications
295 // ---------------------------------------------------------------------------
299 #include <wx/dynlib.h>
305 Helper class to manipulate console from a GUI app.
307 Notice that console output is available in the GUI app only if:
308 - AttachConsole() returns TRUE (which means it never works under pre-XP)
309 - we have a valid STD_ERROR_HANDLE
310 - command history hasn't been changed since our startup
312 To check if all these conditions are verified, you need to simple call
313 IsOkToUse(). It will check the first two conditions above the first time it
314 is called (and if this fails, the subsequent calls will return immediately)
315 and also recheck the last one every time it is called.
317 class wxConsoleStderr
320 // default ctor does nothing, call Init() before using this class
323 m_hStderr
= INVALID_HANDLE_VALUE
;
333 if ( m_hStderr
!= INVALID_HANDLE_VALUE
)
335 if ( !::FreeConsole() )
337 wxLogLastError(wxT("FreeConsole"));
342 // return true if we were successfully initialized and there had been no
343 // console activity which would interfere with our output since then
344 bool IsOkToUse() const
348 wxConsoleStderr
* const self
= const_cast<wxConsoleStderr
*>(this);
349 self
->m_ok
= self
->DoInit();
351 // no need to call IsHistoryUnchanged() as we just initialized
356 return m_ok
&& IsHistoryUnchanged();
360 // output the provided text on the console, return true if ok
361 bool Write(const wxString
& text
);
364 // called by Init() once only to do the real initialization
367 // retrieve the command line history into the provided buffer and return
369 int GetCommandHistory(wxWxCharBuffer
& buf
) const;
371 // check if the console history has changed
372 bool IsHistoryUnchanged() const;
374 int m_ok
; // initially -1, set to true or false by Init()
376 wxDynamicLibrary m_dllKernel32
;
378 HANDLE m_hStderr
; // console handle, if it's valid we must call
379 // FreeConsole() (even if m_ok != 1)
381 wxWxCharBuffer m_history
; // command history on startup
382 int m_historyLen
; // length command history buffer
384 wxCharBuffer m_data
; // data between empty line and cursor position
385 int m_dataLen
; // length data buffer
386 int m_dataLine
; // line offset
388 typedef DWORD (WINAPI
*GetConsoleCommandHistory_t
)(LPTSTR sCommands
,
391 typedef DWORD (WINAPI
*GetConsoleCommandHistoryLength_t
)(LPCTSTR sExeName
);
393 GetConsoleCommandHistory_t m_pfnGetConsoleCommandHistory
;
394 GetConsoleCommandHistoryLength_t m_pfnGetConsoleCommandHistoryLength
;
396 wxDECLARE_NO_COPY_CLASS(wxConsoleStderr
);
399 bool wxConsoleStderr::DoInit()
401 HANDLE hStderr
= ::GetStdHandle(STD_ERROR_HANDLE
);
403 if ( hStderr
== INVALID_HANDLE_VALUE
|| !hStderr
)
406 if ( !m_dllKernel32
.Load(wxT("kernel32.dll")) )
409 typedef BOOL (WINAPI
*AttachConsole_t
)(DWORD dwProcessId
);
410 AttachConsole_t
wxDL_INIT_FUNC(pfn
, AttachConsole
, m_dllKernel32
);
412 if ( !pfnAttachConsole
|| !pfnAttachConsole(ATTACH_PARENT_PROCESS
) )
415 // console attached, set m_hStderr now to ensure that we free it in the
419 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistory
, m_dllKernel32
);
420 if ( !m_pfnGetConsoleCommandHistory
)
423 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistoryLength
, m_dllKernel32
);
424 if ( !m_pfnGetConsoleCommandHistoryLength
)
427 // remember the current command history to be able to compare with it later
428 // in IsHistoryUnchanged()
429 m_historyLen
= GetCommandHistory(m_history
);
434 // now find the first blank line above the current position
435 CONSOLE_SCREEN_BUFFER_INFO csbi
;
437 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
439 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
445 pos
.Y
= csbi
.dwCursorPosition
.Y
+ 1;
447 // we decide that a line is empty if first 4 characters are spaces
453 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, buf
, WXSIZEOF(buf
),
456 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
459 } while ( wxStrncmp(" ", buf
, WXSIZEOF(buf
)) != 0 );
461 // calculate line offset and length of data
462 m_dataLine
= csbi
.dwCursorPosition
.Y
- pos
.Y
;
463 m_dataLen
= m_dataLine
*csbi
.dwMaximumWindowSize
.X
+ csbi
.dwCursorPosition
.X
;
467 m_data
.extend(m_dataLen
);
468 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, m_data
.data(), m_dataLen
,
471 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
479 int wxConsoleStderr::GetCommandHistory(wxWxCharBuffer
& buf
) const
481 // these functions are internal and may only be called by cmd.exe
482 static const wxChar
*CMD_EXE
= wxT("cmd.exe");
484 const int len
= m_pfnGetConsoleCommandHistoryLength(CMD_EXE
);
489 int len2
= m_pfnGetConsoleCommandHistory(buf
.data(), len
, CMD_EXE
);
492 // there seems to be a bug in the GetConsoleCommandHistoryA(), it
493 // returns the length of Unicode string and not ANSI one
495 #endif // !wxUSE_UNICODE
499 wxFAIL_MSG( wxT("failed getting history?") );
506 bool wxConsoleStderr::IsHistoryUnchanged() const
508 wxASSERT_MSG( m_ok
== 1, wxT("shouldn't be called if not initialized") );
510 // get (possibly changed) command history
511 wxWxCharBuffer history
;
512 const int historyLen
= GetCommandHistory(history
);
514 // and compare it with the original one
515 return historyLen
== m_historyLen
&& history
&&
516 memcmp(m_history
, history
, historyLen
) == 0;
519 bool wxConsoleStderr::Write(const wxString
& text
)
521 wxASSERT_MSG( m_hStderr
!= INVALID_HANDLE_VALUE
,
522 wxT("should only be called if Init() returned true") );
524 // get current position
525 CONSOLE_SCREEN_BUFFER_INFO csbi
;
526 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
528 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
532 // and calculate new position (where is empty line)
533 csbi
.dwCursorPosition
.X
= 0;
534 csbi
.dwCursorPosition
.Y
-= m_dataLine
;
536 if ( !::SetConsoleCursorPosition(m_hStderr
, csbi
.dwCursorPosition
) )
538 wxLogLastError(wxT("SetConsoleCursorPosition"));
543 if ( !::FillConsoleOutputCharacter(m_hStderr
, wxT(' '), m_dataLen
,
544 csbi
.dwCursorPosition
, &ret
) )
546 wxLogLastError(wxT("FillConsoleOutputCharacter"));
550 if ( !::WriteConsole(m_hStderr
, text
.wx_str(), text
.length(), &ret
, NULL
) )
552 wxLogLastError(wxT("WriteConsole"));
556 WriteConsoleA(m_hStderr
, m_data
, m_dataLen
, &ret
, 0);
561 wxConsoleStderr s_consoleStderr
;
563 } // anonymous namespace
565 bool wxGUIAppTraits::CanUseStderr()
567 return s_consoleStderr
.IsOkToUse();
570 bool wxGUIAppTraits::WriteToStderr(const wxString
& text
)
572 return s_consoleStderr
.IsOkToUse() && s_consoleStderr
.Write(text
);
575 #endif // !__WXWINCE__
577 // ===========================================================================
578 // wxApp implementation
579 // ===========================================================================
581 int wxApp::m_nCmdShow
= SW_SHOWNORMAL
;
583 // ---------------------------------------------------------------------------
585 // ---------------------------------------------------------------------------
587 IMPLEMENT_DYNAMIC_CLASS(wxApp
, wxEvtHandler
)
589 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
590 EVT_IDLE(wxApp::OnIdle
)
591 EVT_END_SESSION(wxApp::OnEndSession
)
592 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession
)
595 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
597 class wxCallBaseCleanup
600 wxCallBaseCleanup(wxApp
*app
) : m_app(app
) { }
601 ~wxCallBaseCleanup() { if ( m_app
) m_app
->wxAppBase::CleanUp(); }
603 void Dismiss() { m_app
= NULL
; }
610 bool wxApp::Initialize(int& argc
, wxChar
**argv
)
612 if ( !wxAppBase::Initialize(argc
, argv
) )
615 // ensure that base cleanup is done if we return too early
616 wxCallBaseCleanup
callBaseCleanup(this);
618 #if !defined(__WXMICROWIN__)
619 InitCommonControls();
620 #endif // !defined(__WXMICROWIN__)
622 #if defined(__SMARTPHONE__) || defined(__POCKETPC__)
623 SHInitExtraControls();
627 // Don't show a message box if a function such as SHGetFileInfo
628 // fails to find a device.
629 SetErrorMode(SEM_FAILCRITICALERRORS
|SEM_NOOPENFILEERRORBOX
);
634 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
635 wxSetKeyboardHook(true);
638 callBaseCleanup
.Dismiss();
643 // ---------------------------------------------------------------------------
644 // Win32 window class registration
645 // ---------------------------------------------------------------------------
648 const wxChar
*wxApp::GetRegisteredClassName(const wxChar
*name
,
652 const size_t count
= gs_regClassesInfo
.size();
653 for ( size_t n
= 0; n
< count
; n
++ )
655 if ( gs_regClassesInfo
[n
].regname
== name
)
656 return gs_regClassesInfo
[n
].regname
.c_str();
659 // we need to register this class
661 wxZeroMemory(wndclass
);
663 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
664 wndclass
.hInstance
= wxGetInstance();
665 wndclass
.hCursor
= ::LoadCursor(NULL
, IDC_ARROW
);
666 wndclass
.hbrBackground
= (HBRUSH
)wxUIntToPtr(bgBrushCol
+ 1);
667 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| extraStyles
;
670 ClassRegInfo
regClass(name
);
671 wndclass
.lpszClassName
= regClass
.regname
.wx_str();
672 if ( !::RegisterClass(&wndclass
) )
674 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
679 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
680 wndclass
.lpszClassName
= regClass
.regnameNR
.wx_str();
681 if ( !::RegisterClass(&wndclass
) )
683 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
685 ::UnregisterClass(regClass
.regname
.c_str(), wxGetInstance());
689 gs_regClassesInfo
.push_back(regClass
);
691 // take care to return the pointer which will remain valid after the
692 // function returns (it could be invalidated later if new elements are
693 // added to the vector and it's reallocated but this shouldn't matter as
694 // this pointer should be used right now, not stored)
695 return gs_regClassesInfo
.back().regname
.wx_str();
698 bool wxApp::IsRegisteredClassName(const wxString
& name
)
700 const size_t count
= gs_regClassesInfo
.size();
701 for ( size_t n
= 0; n
< count
; n
++ )
703 if ( gs_regClassesInfo
[n
].regname
== name
||
704 gs_regClassesInfo
[n
].regnameNR
== name
)
711 void wxApp::UnregisterWindowClasses()
713 const size_t count
= gs_regClassesInfo
.size();
714 for ( size_t n
= 0; n
< count
; n
++ )
716 const ClassRegInfo
& regClass
= gs_regClassesInfo
[n
];
717 if ( !::UnregisterClass(regClass
.regname
.c_str(), wxGetInstance()) )
719 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
723 if ( !::UnregisterClass(regClass
.regnameNR
.c_str(), wxGetInstance()) )
725 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
726 regClass
.regnameNR
));
730 gs_regClassesInfo
.clear();
733 void wxApp::CleanUp()
735 // all objects pending for deletion must be deleted first, otherwise
736 // UnregisterWindowClasses() call wouldn't succeed (because windows
737 // using the classes being unregistered still exist), so call the base
738 // class method first and only then do our clean up
739 wxAppBase::CleanUp();
741 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
742 wxSetKeyboardHook(false);
747 // for an EXE the classes are unregistered when it terminates but DLL may
748 // be loaded several times (load/unload/load) into the same process in
749 // which case the registration will fail after the first time if we don't
750 // unregister the classes now
751 UnregisterWindowClasses();
754 // ----------------------------------------------------------------------------
756 // ----------------------------------------------------------------------------
760 m_printMode
= wxPRINT_WINDOWS
;
767 // ----------------------------------------------------------------------------
768 // wxApp idle handling
769 // ----------------------------------------------------------------------------
771 void wxApp::OnIdle(wxIdleEvent
& WXUNUSED(event
))
773 #if wxUSE_DC_CACHEING
774 // automated DC cache management: clear the cached DCs and bitmap
775 // if it's likely that the app has finished with them, that is, we
776 // get an idle event and we're not dragging anything.
777 if (!::GetKeyState(MK_LBUTTON
) && !::GetKeyState(MK_MBUTTON
) && !::GetKeyState(MK_RBUTTON
))
778 wxMSWDCImpl::ClearCache();
779 #endif // wxUSE_DC_CACHEING
782 void wxApp::WakeUpIdle()
784 // Send the top window a dummy message so idle handler processing will
785 // start up again. Doing it this way ensures that the idle handler
786 // wakes up in the right thread (see also wxWakeUpMainThread() which does
787 // the same for the main app thread only)
788 wxWindow
* const topWindow
= wxTheApp
->GetTopWindow();
791 HWND hwndTop
= GetHwndOf(topWindow
);
793 // Do not post WM_NULL if there's already a pending WM_NULL to avoid
794 // overflowing the message queue.
796 // Notice that due to a limitation of PeekMessage() API (which handles
797 // 0,0 range specially), we have to check the range from 0-1 instead.
798 // This still makes it possible to overflow the queue with WM_NULLs by
799 // interspersing the calles to WakeUpIdle() with windows creation but
800 // it should be rather hard to do it accidentally.
802 if ( !::PeekMessage(&msg
, hwndTop
, 0, 1, PM_NOREMOVE
) ||
803 ::PeekMessage(&msg
, hwndTop
, 1, 1, PM_NOREMOVE
) )
805 if ( !::PostMessage(hwndTop
, WM_NULL
, 0, 0) )
807 // should never happen
808 wxLogLastError(wxT("PostMessage(WM_NULL)"));
814 wxWakeUpMainThread();
815 #endif // wxUSE_THREADS
818 // ----------------------------------------------------------------------------
819 // other wxApp event hanlders
820 // ----------------------------------------------------------------------------
822 void wxApp::OnEndSession(wxCloseEvent
& WXUNUSED(event
))
824 // Windows will terminate the process soon after we return from
825 // WM_ENDSESSION handler or when we delete our last window, so make sure we
826 // at least execute our cleanup code before
828 // prevent the window from being destroyed when the corresponding wxTLW is
829 // destroyed: this will result in a leak of a HWND, of course, but who
830 // cares when the process is being killed anyhow
831 if ( !wxTopLevelWindows
.empty() )
832 wxTopLevelWindows
[0]->SetHWND(0);
834 const int rc
= OnExit();
838 // calling exit() instead of ExitProcess() or not doing anything at all and
839 // being killed by Windows has the advantage of executing the dtors of
844 // Default behaviour: close the application with prompts. The
845 // user can veto the close, and therefore the end session.
846 void wxApp::OnQueryEndSession(wxCloseEvent
& event
)
850 if (!GetTopWindow()->Close(!event
.CanVeto()))
855 // ----------------------------------------------------------------------------
856 // system DLL versions
857 // ----------------------------------------------------------------------------
859 // these functions have trivial inline implementations for CE
862 #if wxUSE_DYNLIB_CLASS
867 // helper function: retrieve the DLL version by using DllGetVersion(), returns
868 // 0 if the DLL doesn't export such function
869 int CallDllGetVersion(wxDynamicLibrary
& dll
)
871 // now check if the function is available during run-time
872 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC
, DllGetVersion
, dll
);
873 if ( !pfnDllGetVersion
)
877 dvi
.cbSize
= sizeof(dvi
);
879 HRESULT hr
= (*pfnDllGetVersion
)(&dvi
);
882 wxLogApiError(wxT("DllGetVersion"), hr
);
887 return 100*dvi
.dwMajorVersion
+ dvi
.dwMinorVersion
;
890 } // anonymous namespace
893 int wxApp::GetComCtl32Version()
897 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
898 // but as its value should be the same both times it doesn't matter
899 static int s_verComCtl32
= -1;
901 if ( s_verComCtl32
== -1 )
903 // we're prepared to handle the errors
906 // we don't want to load comctl32.dll, it should be already loaded but,
907 // depending on the OS version and the presence of the manifest, it can
908 // be either v5 or v6 and instead of trying to guess it just get the
909 // handle of the already loaded version
910 wxLoadedDLL
dllComCtl32(wxT("comctl32.dll"));
911 if ( !dllComCtl32
.IsLoaded() )
917 // try DllGetVersion() for recent DLLs
918 s_verComCtl32
= CallDllGetVersion(dllComCtl32
);
920 // if DllGetVersion() is unavailable either during compile or
921 // run-time, try to guess the version otherwise
922 if ( !s_verComCtl32
)
924 // InitCommonControlsEx is unique to 4.70 and later
925 void *pfn
= dllComCtl32
.GetSymbol(wxT("InitCommonControlsEx"));
928 // not found, must be 4.00
933 // many symbols appeared in comctl32 4.71, could use any of
934 // them except may be DllInstall()
935 pfn
= dllComCtl32
.GetSymbol(wxT("InitializeFlatSB"));
938 // not found, must be 4.70
943 // found, must be 4.71 or later
950 return s_verComCtl32
;
954 int wxApp::GetShell32Version()
956 static int s_verShell32
= -1;
957 if ( s_verShell32
== -1 )
959 // we're prepared to handle the errors
962 wxDynamicLibrary
dllShell32(wxT("shell32.dll"), wxDL_VERBATIM
);
963 if ( dllShell32
.IsLoaded() )
965 s_verShell32
= CallDllGetVersion(dllShell32
);
969 // there doesn't seem to be any way to distinguish between 4.00
970 // and 4.70 (starting from 4.71 we have DllGetVersion()) so
971 // just assume it is 4.0
975 else // failed load the DLL?
984 #else // !wxUSE_DYNLIB_CLASS
987 int wxApp::GetComCtl32Version()
993 int wxApp::GetShell32Version()
998 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
1000 #endif // !__WXWINCE__
1002 #if wxUSE_EXCEPTIONS
1004 // ----------------------------------------------------------------------------
1005 // exception handling
1006 // ----------------------------------------------------------------------------
1008 bool wxApp::OnExceptionInMainLoop()
1010 // ask the user about what to do: use the Win32 API function here as it
1011 // could be dangerous to use any wxWidgets code in this state
1016 wxT("An unhandled exception occurred. Press \"Abort\" to \
1017 terminate the program,\r\n\
1018 \"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
1019 wxT("Unhandled exception"),
1020 MB_ABORTRETRYIGNORE
|
1030 wxFAIL_MSG( wxT("unexpected MessageBox() return code") );
1041 #endif // wxUSE_EXCEPTIONS