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"
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 WXDLLIMPEXP_CORE
const wxChar
*wxCanvasClassName
= NULL
;
120 WXDLLIMPEXP_CORE
const wxChar
*wxCanvasClassNameNR
= NULL
;
121 WXDLLIMPEXP_CORE
const wxChar
*wxMDIFrameClassName
= NULL
;
122 WXDLLIMPEXP_CORE
const wxChar
*wxMDIFrameClassNameNoRedraw
= NULL
;
123 WXDLLIMPEXP_CORE
const wxChar
*wxMDIChildFrameClassName
= NULL
;
124 WXDLLIMPEXP_CORE
const wxChar
*wxMDIChildFrameClassNameNoRedraw
= NULL
;
126 // ----------------------------------------------------------------------------
128 // ----------------------------------------------------------------------------
130 LRESULT WXDLLEXPORT APIENTRY
wxWndProc(HWND
, UINT
, WPARAM
, LPARAM
);
132 // ===========================================================================
133 // wxGUIAppTraits implementation
134 // ===========================================================================
136 // private class which we use to pass parameters from BeforeChildWaitLoop() to
137 // AfterChildWaitLoop()
138 struct ChildWaitLoopData
140 ChildWaitLoopData(wxWindowDisabler
*wd_
, wxWindow
*winActive_
)
143 winActive
= winActive_
;
146 wxWindowDisabler
*wd
;
150 void *wxGUIAppTraits::BeforeChildWaitLoop()
153 We use a dirty hack here to disable all application windows (which we
154 must do because otherwise the calls to wxYield() could lead to some very
155 unexpected reentrancies in the users code) but to avoid losing
156 focus/activation entirely when the child process terminates which would
157 happen if we simply disabled everything using wxWindowDisabler. Indeed,
158 remember that Windows will never activate a disabled window and when the
159 last childs window is closed and Windows looks for a window to activate
160 all our windows are still disabled. There is no way to enable them in
161 time because we don't know when the childs windows are going to be
162 closed, so the solution we use here is to keep one special tiny frame
163 enabled all the time. Then when the child terminates it will get
164 activated and when we close it below -- after reenabling all the other
165 windows! -- the previously active window becomes activated again and
170 // first disable all existing windows
171 wxWindowDisabler
*wd
= new wxWindowDisabler
;
173 // then create an "invisible" frame: it has minimal size, is positioned
174 // (hopefully) outside the screen and doesn't appear on the taskbar
175 wxWindow
*winActive
= new wxFrame
177 wxTheApp
->GetTopWindow(),
180 wxPoint(32600, 32600),
182 wxDEFAULT_FRAME_STYLE
| wxFRAME_NO_TASKBAR
186 return new ChildWaitLoopData(wd
, winActive
);
189 void wxGUIAppTraits::AlwaysYield()
194 void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig
)
198 ChildWaitLoopData
* const data
= (ChildWaitLoopData
*)dataOrig
;
202 // finally delete the dummy frame and, as wd has been already destroyed and
203 // the other windows reenabled, the activation is going to return to the
204 // window which had had it before
205 data
->winActive
->Destroy();
207 // also delete the temporary data object itself
211 bool wxGUIAppTraits::DoMessageFromThreadWait()
213 // we should return false only if the app should exit, i.e. only if
214 // Dispatch() determines that the main event loop should terminate
215 wxEventLoopBase
* const evtLoop
= wxEventLoop::GetActive();
216 if ( !evtLoop
|| !evtLoop
->Pending() )
218 // no events means no quit event
222 return evtLoop
->Dispatch();
225 DWORD
wxGUIAppTraits::WaitForThread(WXHANDLE hThread
)
227 // if we don't have a running event loop, we shouldn't wait for the
228 // messages as we never remove them from the message queue and so we enter
229 // an infinite loop as MsgWaitForMultipleObjects() keeps returning
231 if ( !wxEventLoop::GetActive() )
232 return DoSimpleWaitForThread(hThread
);
234 return ::MsgWaitForMultipleObjects
236 1, // number of objects to wait for
237 (HANDLE
*)&hThread
, // the objects
238 false, // wait for any objects, not all
239 INFINITE
, // no timeout
240 QS_ALLINPUT
| // return as soon as there are any events
245 wxPortId
wxGUIAppTraits::GetToolkitVersion(int *majVer
, int *minVer
) const
250 // on Windows, the toolkit version is the same of the OS version
251 // as Windows integrates the OS kernel with the GUI toolkit.
252 info
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
253 if ( ::GetVersionEx(&info
) )
256 *majVer
= info
.dwMajorVersion
;
258 *minVer
= info
.dwMinorVersion
;
261 #if defined(__WXHANDHELD__) || defined(__WXWINCE__)
270 wxTimerImpl
*wxGUIAppTraits::CreateTimerImpl(wxTimer
*timer
)
272 return new wxMSWTimerImpl(timer
);
275 #endif // wxUSE_TIMER
277 wxEventLoopBase
* wxGUIAppTraits::CreateEventLoop()
279 return new wxEventLoop
;
282 // ---------------------------------------------------------------------------
283 // Stuff for using console from the GUI applications
284 // ---------------------------------------------------------------------------
288 #include <wx/dynlib.h>
294 Helper class to manipulate console from a GUI app.
296 Notice that console output is available in the GUI app only if:
297 - AttachConsole() returns TRUE (which means it never works under pre-XP)
298 - we have a valid STD_ERROR_HANDLE
299 - command history hasn't been changed since our startup
301 To check if all these conditions are verified, you need to simple call
302 IsOkToUse(). It will check the first two conditions above the first time it
303 is called (and if this fails, the subsequent calls will return immediately)
304 and also recheck the last one every time it is called.
306 class wxConsoleStderr
309 // default ctor does nothing, call Init() before using this class
312 m_hStderr
= INVALID_HANDLE_VALUE
;
322 if ( m_hStderr
!= INVALID_HANDLE_VALUE
)
324 if ( !::FreeConsole() )
326 wxLogLastError(_T("FreeConsole"));
331 // return true if we were successfully initialized and there had been no
332 // console activity which would interfere with our output since then
333 bool IsOkToUse() const
337 wxConsoleStderr
* const self
= const_cast<wxConsoleStderr
*>(this);
338 self
->m_ok
= self
->DoInit();
340 // no need to call IsHistoryUnchanged() as we just initialized
345 return m_ok
&& IsHistoryUnchanged();
349 // output the provided text on the console, return true if ok
350 bool Write(const wxString
& text
);
353 // called by Init() once only to do the real initialization
356 // retrieve the command line history into the provided buffer and return
358 int GetCommandHistory(wxWxCharBuffer
& buf
) const;
360 // check if the console history has changed
361 bool IsHistoryUnchanged() const;
363 int m_ok
; // initially -1, set to true or false by Init()
365 wxDynamicLibrary m_dllKernel32
;
367 HANDLE m_hStderr
; // console handle, if it's valid we must call
368 // FreeConsole() (even if m_ok != 1)
370 wxWxCharBuffer m_history
; // command history on startup
371 int m_historyLen
; // length command history buffer
373 wxCharBuffer m_data
; // data between empty line and cursor position
374 int m_dataLen
; // length data buffer
375 int m_dataLine
; // line offset
377 typedef DWORD (WINAPI
*GetConsoleCommandHistory_t
)(LPTSTR sCommands
,
380 typedef DWORD (WINAPI
*GetConsoleCommandHistoryLength_t
)(LPCTSTR sExeName
);
382 GetConsoleCommandHistory_t m_pfnGetConsoleCommandHistory
;
383 GetConsoleCommandHistoryLength_t m_pfnGetConsoleCommandHistoryLength
;
385 DECLARE_NO_COPY_CLASS(wxConsoleStderr
)
388 bool wxConsoleStderr::DoInit()
390 HANDLE hStderr
= ::GetStdHandle(STD_ERROR_HANDLE
);
392 if ( hStderr
== INVALID_HANDLE_VALUE
|| !hStderr
)
395 if ( !m_dllKernel32
.Load(_T("kernel32.dll")) )
398 typedef BOOL (WINAPI
*AttachConsole_t
)(DWORD dwProcessId
);
399 AttachConsole_t
wxDL_INIT_FUNC(pfn
, AttachConsole
, m_dllKernel32
);
401 if ( !pfnAttachConsole
|| !pfnAttachConsole(ATTACH_PARENT_PROCESS
) )
404 // console attached, set m_hStderr now to ensure that we free it in the
408 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistory
, m_dllKernel32
);
409 if ( !m_pfnGetConsoleCommandHistory
)
412 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistoryLength
, m_dllKernel32
);
413 if ( !m_pfnGetConsoleCommandHistoryLength
)
416 // remember the current command history to be able to compare with it later
417 // in IsHistoryUnchanged()
418 m_historyLen
= GetCommandHistory(m_history
);
423 // now find the first blank line above the current position
424 CONSOLE_SCREEN_BUFFER_INFO csbi
;
426 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
428 wxLogLastError(_T("GetConsoleScreenBufferInfo"));
434 pos
.Y
= csbi
.dwCursorPosition
.Y
+ 1;
436 // we decide that a line is empty if first 4 characters are spaces
442 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, buf
, WXSIZEOF(buf
),
445 wxLogLastError(_T("ReadConsoleOutputCharacterA"));
448 } while ( wxStrncmp(" ", buf
, WXSIZEOF(buf
)) != 0 );
450 // calculate line offset and length of data
451 m_dataLine
= csbi
.dwCursorPosition
.Y
- pos
.Y
;
452 m_dataLen
= m_dataLine
*csbi
.dwMaximumWindowSize
.X
+ csbi
.dwCursorPosition
.X
;
456 m_data
.extend(m_dataLen
);
457 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, m_data
.data(), m_dataLen
,
460 wxLogLastError(_T("ReadConsoleOutputCharacterA"));
468 int wxConsoleStderr::GetCommandHistory(wxWxCharBuffer
& buf
) const
470 // these functions are internal and may only be called by cmd.exe
471 static const wxChar
*CMD_EXE
= _T("cmd.exe");
473 const int len
= m_pfnGetConsoleCommandHistoryLength(CMD_EXE
);
478 int len2
= m_pfnGetConsoleCommandHistory(buf
.data(), len
, CMD_EXE
);
481 // there seems to be a bug in the GetConsoleCommandHistoryA(), it
482 // returns the length of Unicode string and not ANSI one
484 #endif // !wxUSE_UNICODE
488 wxFAIL_MSG( _T("failed getting history?") );
495 bool wxConsoleStderr::IsHistoryUnchanged() const
497 wxASSERT_MSG( m_ok
== 1, _T("shouldn't be called if not initialized") );
499 // get (possibly changed) command history
500 wxWxCharBuffer history
;
501 const int historyLen
= GetCommandHistory(history
);
503 // and compare it with the original one
504 return historyLen
== m_historyLen
&& history
&&
505 memcmp(m_history
, history
, historyLen
) == 0;
508 bool wxConsoleStderr::Write(const wxString
& text
)
510 wxASSERT_MSG( m_hStderr
!= INVALID_HANDLE_VALUE
,
511 _T("should only be called if Init() returned true") );
513 // get current position
514 CONSOLE_SCREEN_BUFFER_INFO csbi
;
515 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
517 wxLogLastError(_T("GetConsoleScreenBufferInfo"));
521 // and calculate new position (where is empty line)
522 csbi
.dwCursorPosition
.X
= 0;
523 csbi
.dwCursorPosition
.Y
-= m_dataLine
;
525 if ( !::SetConsoleCursorPosition(m_hStderr
, csbi
.dwCursorPosition
) )
527 wxLogLastError(_T("SetConsoleCursorPosition"));
532 if ( !::FillConsoleOutputCharacter(m_hStderr
, _T(' '), m_dataLen
,
533 csbi
.dwCursorPosition
, &ret
) )
535 wxLogLastError(_T("FillConsoleOutputCharacter"));
539 if ( !::WriteConsole(m_hStderr
, text
.wx_str(), text
.length(), &ret
, NULL
) )
541 wxLogLastError(_T("WriteConsole"));
545 WriteConsoleA(m_hStderr
, m_data
, m_dataLen
, &ret
, 0);
550 wxConsoleStderr s_consoleStderr
;
552 } // anonymous namespace
554 bool wxGUIAppTraits::CanUseStderr()
556 return s_consoleStderr
.IsOkToUse();
559 bool wxGUIAppTraits::WriteToStderr(const wxString
& text
)
561 return s_consoleStderr
.IsOkToUse() && s_consoleStderr
.Write(text
);
564 #endif // !__WXWINCE__
566 // ===========================================================================
567 // wxApp implementation
568 // ===========================================================================
570 int wxApp::m_nCmdShow
= SW_SHOWNORMAL
;
572 // ---------------------------------------------------------------------------
574 // ---------------------------------------------------------------------------
576 IMPLEMENT_DYNAMIC_CLASS(wxApp
, wxEvtHandler
)
578 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
579 EVT_IDLE(wxApp::OnIdle
)
580 EVT_END_SESSION(wxApp::OnEndSession
)
581 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession
)
584 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
586 class wxCallBaseCleanup
589 wxCallBaseCleanup(wxApp
*app
) : m_app(app
) { }
590 ~wxCallBaseCleanup() { if ( m_app
) m_app
->wxAppBase::CleanUp(); }
592 void Dismiss() { m_app
= NULL
; }
599 bool wxApp::Initialize(int& argc
, wxChar
**argv
)
601 if ( !wxAppBase::Initialize(argc
, argv
) )
604 // ensure that base cleanup is done if we return too early
605 wxCallBaseCleanup
callBaseCleanup(this);
607 #if !defined(__WXMICROWIN__)
608 InitCommonControls();
609 #endif // !defined(__WXMICROWIN__)
611 #if defined(__SMARTPHONE__) || defined(__POCKETPC__)
612 SHInitExtraControls();
616 // Don't show a message box if a function such as SHGetFileInfo
617 // fails to find a device.
618 SetErrorMode(SEM_FAILCRITICALERRORS
|SEM_NOOPENFILEERRORBOX
);
623 RegisterWindowClasses();
625 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
626 wxSetKeyboardHook(true);
629 callBaseCleanup
.Dismiss();
634 // ---------------------------------------------------------------------------
635 // RegisterWindowClasses
636 // ---------------------------------------------------------------------------
638 // This function registers the given class name and stores a pointer to a
639 // heap-allocated copy of it at the specified location, it must be deleted
641 static void RegisterAndStoreClassName(const wxString
& uniqueClassName
,
642 const wxChar
**className
,
643 WNDCLASS
*lpWndClass
)
645 const size_t length
= uniqueClassName
.length() + 1; // for trailing NUL
646 wxChar
* const newChars
= new wxChar
[length
];
647 wxStrlcpy(newChars
, uniqueClassName
, length
);
648 *className
= newChars
;
649 lpWndClass
->lpszClassName
= *className
;
651 if ( !::RegisterClass(lpWndClass
) )
653 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"), newChars
));
657 // This function registers the class defined by the provided WNDCLASS struct
658 // contents using a unique name constructed from the specified base name and
659 // and a suffix unique to this library instance. It also stores the generated
660 // unique names for normal and "no redraw" versions of the class in the
661 // provided variables, caller must delete their contents later.
662 static void RegisterClassWithUniqueNames(const wxString
& baseName
,
663 const wxChar
**className
,
664 const wxChar
**classNameNR
,
665 WNDCLASS
*lpWndClass
)
667 // for each class we register one with CS_(V|H)REDRAW style and one
668 // without for windows created with wxNO_FULL_REDRAW_ON_REPAINT flag
669 static const long styleNormal
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
;
670 static const long styleNoRedraw
= CS_DBLCLKS
;
672 const wxString
uniqueSuffix(wxString::Format(wxT("@%p"), className
));
674 wxString
uniqueClassName(baseName
+ uniqueSuffix
);
675 lpWndClass
->style
= styleNormal
;
676 RegisterAndStoreClassName(uniqueClassName
, className
, lpWndClass
);
678 // NB: remember that code elsewhere supposes that no redraw class names
679 // use the same names as normal classes with "NR" suffix so we must put
680 // "NR" at the end instead of using more natural baseName+"NR"+suffix
681 wxString
uniqueClassNameNR(uniqueClassName
+ wxT("NR"));
682 lpWndClass
->style
= styleNoRedraw
;
683 RegisterAndStoreClassName(uniqueClassNameNR
, classNameNR
, lpWndClass
);
686 // TODO we should only register classes really used by the app. For this it
687 // would be enough to just delay the class registration until an attempt
688 // to create a window of this class is made.
689 bool wxApp::RegisterWindowClasses()
692 wxZeroMemory(wndclass
);
694 // the fields which are common to all classes
695 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
696 wndclass
.hInstance
= wxhInstance
;
697 wndclass
.hCursor
= ::LoadCursor((HINSTANCE
)NULL
, IDC_ARROW
);
699 // register the class for all normal windows and "no redraw" frames
700 wndclass
.hbrBackground
= (HBRUSH
)(COLOR_BTNFACE
+ 1);
701 RegisterClassWithUniqueNames(wxT("wxWindowClass"),
703 &wxCanvasClassNameNR
,
706 // Register the MDI frame window class and "no redraw" MDI frame
707 wndclass
.hbrBackground
= (HBRUSH
)NULL
; // paint MDI frame ourselves
708 RegisterClassWithUniqueNames(wxT("wxMDIFrameClass"),
709 &wxMDIFrameClassName
,
710 &wxMDIFrameClassNameNoRedraw
,
713 // Register the MDI child frame window class and "no redraw" MDI child frame
714 wndclass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
715 RegisterClassWithUniqueNames(wxT("wxMDIChildFrameClass"),
716 &wxMDIChildFrameClassName
,
717 &wxMDIChildFrameClassNameNoRedraw
,
723 // ---------------------------------------------------------------------------
724 // UnregisterWindowClasses
725 // ---------------------------------------------------------------------------
727 // This function unregisters the class with the given name and frees memory
728 // allocated for it by RegisterAndStoreClassName().
729 static bool UnregisterAndFreeClassName(const wxChar
**ppClassName
)
733 if ( !::UnregisterClass(*ppClassName
, wxhInstance
) )
736 wxString::Format(wxT("UnregisterClass(%s)"), *ppClassName
));
741 delete [] (wxChar
*) *ppClassName
;
747 bool wxApp::UnregisterWindowClasses()
751 #ifndef __WXMICROWIN__
752 if ( !UnregisterAndFreeClassName(&wxMDIFrameClassName
) )
755 if ( !UnregisterAndFreeClassName(&wxMDIFrameClassNameNoRedraw
) )
758 if ( !UnregisterAndFreeClassName(&wxMDIChildFrameClassName
) )
761 if ( !UnregisterAndFreeClassName(&wxMDIChildFrameClassNameNoRedraw
) )
764 if ( !UnregisterAndFreeClassName(&wxCanvasClassName
) )
767 if ( !UnregisterAndFreeClassName(&wxCanvasClassNameNR
) )
769 #endif // __WXMICROWIN__
774 void wxApp::CleanUp()
776 // all objects pending for deletion must be deleted first, otherwise
777 // UnregisterWindowClasses() call wouldn't succeed (because windows
778 // using the classes being unregistered still exist), so call the base
779 // class method first and only then do our clean up
780 wxAppBase::CleanUp();
782 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
783 wxSetKeyboardHook(false);
788 // for an EXE the classes are unregistered when it terminates but DLL may
789 // be loaded several times (load/unload/load) into the same process in
790 // which case the registration will fail after the first time if we don't
791 // unregister the classes now
792 UnregisterWindowClasses();
795 // ----------------------------------------------------------------------------
797 // ----------------------------------------------------------------------------
801 m_printMode
= wxPRINT_WINDOWS
;
808 // ----------------------------------------------------------------------------
809 // wxApp idle handling
810 // ----------------------------------------------------------------------------
812 void wxApp::OnIdle(wxIdleEvent
& WXUNUSED(event
))
814 #if wxUSE_DC_CACHEING
815 // automated DC cache management: clear the cached DCs and bitmap
816 // if it's likely that the app has finished with them, that is, we
817 // get an idle event and we're not dragging anything.
818 if (!::GetKeyState(MK_LBUTTON
) && !::GetKeyState(MK_MBUTTON
) && !::GetKeyState(MK_RBUTTON
))
819 wxMSWDCImpl::ClearCache();
820 #endif // wxUSE_DC_CACHEING
823 void wxApp::WakeUpIdle()
825 // Send the top window a dummy message so idle handler processing will
826 // start up again. Doing it this way ensures that the idle handler
827 // wakes up in the right thread (see also wxWakeUpMainThread() which does
828 // the same for the main app thread only)
829 wxWindow
* const topWindow
= wxTheApp
->GetTopWindow();
832 HWND hwndTop
= GetHwndOf(topWindow
);
834 // Do not post WM_NULL if there's already a pending WM_NULL to avoid
835 // overflowing the message queue.
837 // Notice that due to a limitation of PeekMessage() API (which handles
838 // 0,0 range specially), we have to check the range from 0-1 instead.
839 // This still makes it possible to overflow the queue with WM_NULLs by
840 // interspersing the calles to WakeUpIdle() with windows creation but
841 // it should be rather hard to do it accidentally.
843 if ( !::PeekMessage(&msg
, hwndTop
, 0, 1, PM_NOREMOVE
) ||
844 ::PeekMessage(&msg
, hwndTop
, 1, 1, PM_NOREMOVE
) )
846 if ( !::PostMessage(hwndTop
, WM_NULL
, 0, 0) )
848 // should never happen
849 wxLogLastError(wxT("PostMessage(WM_NULL)"));
855 // ----------------------------------------------------------------------------
856 // other wxApp event hanlders
857 // ----------------------------------------------------------------------------
859 void wxApp::OnEndSession(wxCloseEvent
& WXUNUSED(event
))
861 // Windows will terminate the process soon after we return from
862 // WM_ENDSESSION handler or when we delete our last window, so make sure we
863 // at least execute our cleanup code before
865 // prevent the window from being destroyed when the corresponding wxTLW is
866 // destroyed: this will result in a leak of a HWND, of course, but who
867 // cares when the process is being killed anyhow
868 if ( !wxTopLevelWindows
.empty() )
869 wxTopLevelWindows
[0]->SetHWND(0);
871 const int rc
= OnExit();
875 // calling exit() instead of ExitProcess() or not doing anything at all and
876 // being killed by Windows has the advantage of executing the dtors of
881 // Default behaviour: close the application with prompts. The
882 // user can veto the close, and therefore the end session.
883 void wxApp::OnQueryEndSession(wxCloseEvent
& event
)
887 if (!GetTopWindow()->Close(!event
.CanVeto()))
892 // ----------------------------------------------------------------------------
893 // system DLL versions
894 // ----------------------------------------------------------------------------
896 // these functions have trivial inline implementations for CE
899 #if wxUSE_DYNLIB_CLASS
904 // helper function: retrieve the DLL version by using DllGetVersion(), returns
905 // 0 if the DLL doesn't export such function
906 int CallDllGetVersion(wxDynamicLibrary
& dll
)
908 // now check if the function is available during run-time
909 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC
, DllGetVersion
, dll
);
910 if ( !pfnDllGetVersion
)
914 dvi
.cbSize
= sizeof(dvi
);
916 HRESULT hr
= (*pfnDllGetVersion
)(&dvi
);
919 wxLogApiError(_T("DllGetVersion"), hr
);
924 return 100*dvi
.dwMajorVersion
+ dvi
.dwMinorVersion
;
927 } // anonymous namespace
930 int wxApp::GetComCtl32Version()
934 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
935 // but as its value should be the same both times it doesn't matter
936 static int s_verComCtl32
= -1;
938 if ( s_verComCtl32
== -1 )
940 // we're prepared to handle the errors
943 // we don't want to load comctl32.dll, it should be already loaded but,
944 // depending on the OS version and the presence of the manifest, it can
945 // be either v5 or v6 and instead of trying to guess it just get the
946 // handle of the already loaded version
947 wxDynamicLibrary
dllComCtl32(_T("comctl32.dll"),
951 if ( !dllComCtl32
.IsLoaded() )
957 // try DllGetVersion() for recent DLLs
958 s_verComCtl32
= CallDllGetVersion(dllComCtl32
);
960 // if DllGetVersion() is unavailable either during compile or
961 // run-time, try to guess the version otherwise
962 if ( !s_verComCtl32
)
964 // InitCommonControlsEx is unique to 4.70 and later
965 void *pfn
= dllComCtl32
.GetSymbol(_T("InitCommonControlsEx"));
968 // not found, must be 4.00
973 // many symbols appeared in comctl32 4.71, could use any of
974 // them except may be DllInstall()
975 pfn
= dllComCtl32
.GetSymbol(_T("InitializeFlatSB"));
978 // not found, must be 4.70
983 // found, must be 4.71 or later
989 // we shouldn't unload it here as we didn't really load it above
990 dllComCtl32
.Detach();
993 return s_verComCtl32
;
997 int wxApp::GetShell32Version()
999 static int s_verShell32
= -1;
1000 if ( s_verShell32
== -1 )
1002 // we're prepared to handle the errors
1005 wxDynamicLibrary
dllShell32(_T("shell32.dll"), wxDL_VERBATIM
);
1006 if ( dllShell32
.IsLoaded() )
1008 s_verShell32
= CallDllGetVersion(dllShell32
);
1010 if ( !s_verShell32
)
1012 // there doesn't seem to be any way to distinguish between 4.00
1013 // and 4.70 (starting from 4.71 we have DllGetVersion()) so
1014 // just assume it is 4.0
1018 else // failed load the DLL?
1024 return s_verShell32
;
1027 #else // !wxUSE_DYNLIB_CLASS
1030 int wxApp::GetComCtl32Version()
1036 int wxApp::GetShell32Version()
1041 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
1043 #endif // !__WXWINCE__
1045 // ----------------------------------------------------------------------------
1046 // Yield to incoming messages
1047 // ----------------------------------------------------------------------------
1049 bool wxApp::Yield(bool onlyIfNeeded
)
1052 static bool s_inYield
= false;
1056 if ( !onlyIfNeeded
)
1058 wxFAIL_MSG( wxT("wxYield called recursively" ) );
1064 // set the flag and don't forget to reset it before returning
1066 wxON_BLOCK_EXIT_SET(s_inYield
, false);
1070 // disable log flushing from here because a call to wxYield() shouldn't
1071 // normally result in message boxes popping up &c
1074 // ensure the logs will be flashed again when we exit
1075 wxON_BLOCK_EXIT0(wxLog::Resume
);
1079 // we don't want to process WM_QUIT from here - it should be processed in
1080 // the main event loop in order to stop it
1081 wxEventLoopGuarantor dummyLoopIfNeeded
;
1083 while ( PeekMessage(&msg
, (HWND
)0, 0, 0, PM_NOREMOVE
) &&
1084 msg
.message
!= WM_QUIT
)
1087 wxMutexGuiLeaveOrEnter();
1088 #endif // wxUSE_THREADS
1090 if ( !wxTheApp
->Dispatch() )
1094 // if there are pending events, we must process them.
1095 ProcessPendingEvents();
1100 #if wxUSE_EXCEPTIONS
1102 // ----------------------------------------------------------------------------
1103 // exception handling
1104 // ----------------------------------------------------------------------------
1106 bool wxApp::OnExceptionInMainLoop()
1108 // ask the user about what to do: use the Win32 API function here as it
1109 // could be dangerous to use any wxWidgets code in this state
1114 _T("An unhandled exception occurred. Press \"Abort\" to \
1115 terminate the program,\r\n\
1116 \"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
1117 _T("Unhandled exception"),
1118 MB_ABORTRETRYIGNORE
|
1128 wxFAIL_MSG( _T("unexpected MessageBox() return code") );
1139 #endif // wxUSE_EXCEPTIONS