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"
54 #include "wx/msw/private.h"
55 #include "wx/msw/dc.h"
56 #include "wx/msw/ole/oleutils.h"
57 #include "wx/msw/private/timer.h"
60 #include "wx/tooltip.h"
61 #endif // wxUSE_TOOLTIPS
63 // OLE is used for drag-and-drop, clipboard, OLE Automation..., but some
64 // compilers don't support it (missing headers, libs, ...)
65 #if defined(__GNUWIN32_OLD__) || defined(__SYMANTEC__)
69 #endif // broken compilers
71 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
83 #include "wx/msw/missing.h"
85 // instead of including <shlwapi.h> which is not part of the core SDK and not
86 // shipped at all with other compilers, we always define the parts of it we
87 // need here ourselves
89 // NB: DLLVER_PLATFORM_WINDOWS will be defined if shlwapi.h had been somehow
91 #ifndef DLLVER_PLATFORM_WINDOWS
92 // hopefully we don't need to change packing as DWORDs should be already
97 DWORD dwMajorVersion
; // Major version
98 DWORD dwMinorVersion
; // Minor version
99 DWORD dwBuildNumber
; // Build number
100 DWORD dwPlatformID
; // DLLVER_PLATFORM_*
103 typedef HRESULT (CALLBACK
* DLLGETVERSIONPROC
)(DLLVERSIONINFO
*);
104 #endif // defined(DLLVERSIONINFO)
106 #ifndef ATTACH_PARENT_PROCESS
107 #define ATTACH_PARENT_PROCESS ((DWORD)-1)
110 // ---------------------------------------------------------------------------
112 // ---------------------------------------------------------------------------
114 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
115 extern void wxSetKeyboardHook(bool doIt
);
118 // NB: all "NoRedraw" classes must have the same names as the "normal" classes
119 // with NR suffix - wxWindow::MSWCreate() supposes this
121 WXDLLIMPEXP_CORE wxChar
*wxCanvasClassName
;
122 WXDLLIMPEXP_CORE wxChar
*wxCanvasClassNameNR
;
124 WXDLLIMPEXP_CORE
const wxChar
*wxCanvasClassName
= NULL
;
125 WXDLLIMPEXP_CORE
const wxChar
*wxCanvasClassNameNR
= NULL
;
127 WXDLLIMPEXP_CORE
const wxChar
*wxMDIFrameClassName
= NULL
;
128 WXDLLIMPEXP_CORE
const wxChar
*wxMDIFrameClassNameNoRedraw
= NULL
;
129 WXDLLIMPEXP_CORE
const wxChar
*wxMDIChildFrameClassName
= NULL
;
130 WXDLLIMPEXP_CORE
const wxChar
*wxMDIChildFrameClassNameNoRedraw
= NULL
;
132 // ----------------------------------------------------------------------------
134 // ----------------------------------------------------------------------------
136 LRESULT WXDLLEXPORT APIENTRY
wxWndProc(HWND
, UINT
, WPARAM
, LPARAM
);
138 // ===========================================================================
139 // wxGUIAppTraits implementation
140 // ===========================================================================
142 // private class which we use to pass parameters from BeforeChildWaitLoop() to
143 // AfterChildWaitLoop()
144 struct ChildWaitLoopData
146 ChildWaitLoopData(wxWindowDisabler
*wd_
, wxWindow
*winActive_
)
149 winActive
= winActive_
;
152 wxWindowDisabler
*wd
;
156 void *wxGUIAppTraits::BeforeChildWaitLoop()
159 We use a dirty hack here to disable all application windows (which we
160 must do because otherwise the calls to wxYield() could lead to some very
161 unexpected reentrancies in the users code) but to avoid losing
162 focus/activation entirely when the child process terminates which would
163 happen if we simply disabled everything using wxWindowDisabler. Indeed,
164 remember that Windows will never activate a disabled window and when the
165 last childs window is closed and Windows looks for a window to activate
166 all our windows are still disabled. There is no way to enable them in
167 time because we don't know when the childs windows are going to be
168 closed, so the solution we use here is to keep one special tiny frame
169 enabled all the time. Then when the child terminates it will get
170 activated and when we close it below -- after reenabling all the other
171 windows! -- the previously active window becomes activated again and
176 // first disable all existing windows
177 wxWindowDisabler
*wd
= new wxWindowDisabler
;
179 // then create an "invisible" frame: it has minimal size, is positioned
180 // (hopefully) outside the screen and doesn't appear on the taskbar
181 wxWindow
*winActive
= new wxFrame
183 wxTheApp
->GetTopWindow(),
186 wxPoint(32600, 32600),
188 wxDEFAULT_FRAME_STYLE
| wxFRAME_NO_TASKBAR
192 return new ChildWaitLoopData(wd
, winActive
);
195 void wxGUIAppTraits::AlwaysYield()
200 void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig
)
204 ChildWaitLoopData
* const data
= (ChildWaitLoopData
*)dataOrig
;
208 // finally delete the dummy frame and, as wd has been already destroyed and
209 // the other windows reenabled, the activation is going to return to the
210 // window which had had it before
211 data
->winActive
->Destroy();
213 // also delete the temporary data object itself
217 bool wxGUIAppTraits::DoMessageFromThreadWait()
219 // we should return false only if the app should exit, i.e. only if
220 // Dispatch() determines that the main event loop should terminate
221 wxEventLoopBase
* const evtLoop
= wxEventLoop::GetActive();
222 if ( !evtLoop
|| !evtLoop
->Pending() )
224 // no events means no quit event
228 return evtLoop
->Dispatch();
231 DWORD
wxGUIAppTraits::WaitForThread(WXHANDLE hThread
)
233 // if we don't have a running event loop, we shouldn't wait for the
234 // messages as we never remove them from the message queue and so we enter
235 // an infinite loop as MsgWaitForMultipleObjects() keeps returning
237 if ( !wxEventLoop::GetActive() )
238 return DoSimpleWaitForThread(hThread
);
240 return ::MsgWaitForMultipleObjects
242 1, // number of objects to wait for
243 (HANDLE
*)&hThread
, // the objects
244 false, // wait for any objects, not all
245 INFINITE
, // no timeout
246 QS_ALLINPUT
| // return as soon as there are any events
251 wxPortId
wxGUIAppTraits::GetToolkitVersion(int *majVer
, int *minVer
) const
256 // on Windows, the toolkit version is the same of the OS version
257 // as Windows integrates the OS kernel with the GUI toolkit.
258 info
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
259 if ( ::GetVersionEx(&info
) )
262 *majVer
= info
.dwMajorVersion
;
264 *minVer
= info
.dwMinorVersion
;
267 #if defined(__WXHANDHELD__) || defined(__WXWINCE__)
276 wxTimerImpl
*wxGUIAppTraits::CreateTimerImpl(wxTimer
*timer
)
278 return new wxMSWTimerImpl(timer
);
281 #endif // wxUSE_TIMER
283 wxEventLoopBase
* wxGUIAppTraits::CreateEventLoop()
285 return new wxEventLoop
;
288 // ---------------------------------------------------------------------------
289 // Stuff for using console from the GUI applications
290 // ---------------------------------------------------------------------------
294 #include <wx/dynlib.h>
300 Helper class to manipulate console from a GUI app.
302 Notice that console output is available in the GUI app only if:
303 - AttachConsole() returns TRUE (which means it never works under pre-XP)
304 - we have a valid STD_ERROR_HANDLE
305 - command history hasn't been changed since our startup
307 To check if all these conditions are verified, you need to simple call
308 IsOkToUse(). It will check the first two conditions above the first time it
309 is called (and if this fails, the subsequent calls will return immediately)
310 and also recheck the last one every time it is called.
312 class wxConsoleStderr
315 // default ctor does nothing, call Init() before using this class
318 m_hStderr
= INVALID_HANDLE_VALUE
;
328 if ( m_hStderr
!= INVALID_HANDLE_VALUE
)
330 if ( !::FreeConsole() )
332 wxLogLastError(_T("FreeConsole"));
337 // return true if we were successfully initialized and there had been no
338 // console activity which would interfere with our output since then
339 bool IsOkToUse() const
343 wxConsoleStderr
* const self
= wx_const_cast(wxConsoleStderr
*, this);
344 self
->m_ok
= self
->DoInit();
346 // no need to call IsHistoryUnchanged() as we just initialized
351 return m_ok
&& IsHistoryUnchanged();
355 // output the provided text on the console, return true if ok
356 bool Write(const wxString
& text
);
359 // called by Init() once only to do the real initialization
362 // retrieve the command line history into the provided buffer and return
364 int GetCommandHistory(wxWxCharBuffer
& buf
) const;
366 // check if the console history has changed
367 bool IsHistoryUnchanged() const;
369 int m_ok
; // initially -1, set to true or false by Init()
371 wxDynamicLibrary m_dllKernel32
;
373 HANDLE m_hStderr
; // console handle, if it's valid we must call
374 // FreeConsole() (even if m_ok != 1)
376 wxWxCharBuffer m_history
; // command history on startup
377 int m_historyLen
; // length command history buffer
379 wxCharBuffer m_data
; // data between empty line and cursor position
380 int m_dataLen
; // length data buffer
381 int m_dataLine
; // line offset
383 typedef DWORD (WINAPI
*GetConsoleCommandHistory_t
)(LPTSTR sCommands
,
386 typedef DWORD (WINAPI
*GetConsoleCommandHistoryLength_t
)(LPCTSTR sExeName
);
388 GetConsoleCommandHistory_t m_pfnGetConsoleCommandHistory
;
389 GetConsoleCommandHistoryLength_t m_pfnGetConsoleCommandHistoryLength
;
391 DECLARE_NO_COPY_CLASS(wxConsoleStderr
)
394 bool wxConsoleStderr::DoInit()
396 HANDLE hStderr
= ::GetStdHandle(STD_ERROR_HANDLE
);
398 if ( hStderr
== INVALID_HANDLE_VALUE
|| !hStderr
)
401 if ( !m_dllKernel32
.Load(_T("kernel32.dll")) )
404 typedef BOOL (WINAPI
*AttachConsole_t
)(DWORD dwProcessId
);
405 AttachConsole_t
wxDL_INIT_FUNC(pfn
, AttachConsole
, m_dllKernel32
);
407 if ( !pfnAttachConsole
|| !pfnAttachConsole(ATTACH_PARENT_PROCESS
) )
410 // console attached, set m_hStderr now to ensure that we free it in the
414 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistory
, m_dllKernel32
);
415 if ( !m_pfnGetConsoleCommandHistory
)
418 wxDL_INIT_FUNC_AW(m_pfn
, GetConsoleCommandHistoryLength
, m_dllKernel32
);
419 if ( !m_pfnGetConsoleCommandHistoryLength
)
422 // remember the current command history to be able to compare with it later
423 // in IsHistoryUnchanged()
424 m_historyLen
= GetCommandHistory(m_history
);
429 // now find the first blank line above the current position
430 CONSOLE_SCREEN_BUFFER_INFO csbi
;
432 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
434 wxLogLastError(_T("GetConsoleScreenBufferInfo"));
440 pos
.Y
= csbi
.dwCursorPosition
.Y
+ 1;
442 // we decide that a line is empty if first 4 characters are spaces
448 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, buf
, WXSIZEOF(buf
),
451 wxLogLastError(_T("ReadConsoleOutputCharacterA"));
454 } while ( wxStrncmp(" ", buf
, WXSIZEOF(buf
)) != 0 );
456 // calculate line offset and length of data
457 m_dataLine
= csbi
.dwCursorPosition
.Y
- pos
.Y
;
458 m_dataLen
= m_dataLine
*csbi
.dwMaximumWindowSize
.X
+ csbi
.dwCursorPosition
.X
;
462 m_data
.extend(m_dataLen
);
463 if ( !::ReadConsoleOutputCharacterA(m_hStderr
, m_data
.data(), m_dataLen
,
466 wxLogLastError(_T("ReadConsoleOutputCharacterA"));
474 int wxConsoleStderr::GetCommandHistory(wxWxCharBuffer
& buf
) const
476 // these functions are internal and may only be called by cmd.exe
477 static const wxChar
*CMD_EXE
= _T("cmd.exe");
479 const int len
= m_pfnGetConsoleCommandHistoryLength(CMD_EXE
);
484 int len2
= m_pfnGetConsoleCommandHistory(buf
.data(), len
, CMD_EXE
);
487 // there seems to be a bug in the GetConsoleCommandHistoryA(), it
488 // returns the length of Unicode string and not ANSI one
490 #endif // !wxUSE_UNICODE
494 wxFAIL_MSG( _T("failed getting history?") );
501 bool wxConsoleStderr::IsHistoryUnchanged() const
503 wxASSERT_MSG( m_ok
== 1, _T("shouldn't be called if not initialized") );
505 // get (possibly changed) command history
506 wxWxCharBuffer history
;
507 const int historyLen
= GetCommandHistory(history
);
509 // and compare it with the original one
510 return historyLen
== m_historyLen
&& history
&&
511 memcmp(m_history
, history
, historyLen
) == 0;
514 bool wxConsoleStderr::Write(const wxString
& text
)
516 wxASSERT_MSG( m_hStderr
!= INVALID_HANDLE_VALUE
,
517 _T("should only be called if Init() returned true") );
519 // get current position
520 CONSOLE_SCREEN_BUFFER_INFO csbi
;
521 if ( !::GetConsoleScreenBufferInfo(m_hStderr
, &csbi
) )
523 wxLogLastError(_T("GetConsoleScreenBufferInfo"));
527 // and calculate new position (where is empty line)
528 csbi
.dwCursorPosition
.X
= 0;
529 csbi
.dwCursorPosition
.Y
-= m_dataLine
;
531 if ( !::SetConsoleCursorPosition(m_hStderr
, csbi
.dwCursorPosition
) )
533 wxLogLastError(_T("SetConsoleCursorPosition"));
538 if ( !::FillConsoleOutputCharacter(m_hStderr
, _T(' '), m_dataLen
,
539 csbi
.dwCursorPosition
, &ret
) )
541 wxLogLastError(_T("FillConsoleOutputCharacter"));
545 if ( !::WriteConsole(m_hStderr
, text
.wx_str(), text
.length(), &ret
, NULL
) )
547 wxLogLastError(_T("WriteConsole"));
551 WriteConsoleA(m_hStderr
, m_data
, m_dataLen
, &ret
, 0);
556 wxConsoleStderr s_consoleStderr
;
558 } // anonymous namespace
560 bool wxGUIAppTraits::CanUseStderr()
562 return s_consoleStderr
.IsOkToUse();
565 bool wxGUIAppTraits::WriteToStderr(const wxString
& text
)
567 return s_consoleStderr
.IsOkToUse() && s_consoleStderr
.Write(text
);
570 #endif // !__WXWINCE__
572 // ===========================================================================
573 // wxApp implementation
574 // ===========================================================================
576 int wxApp::m_nCmdShow
= SW_SHOWNORMAL
;
578 // ---------------------------------------------------------------------------
580 // ---------------------------------------------------------------------------
582 IMPLEMENT_DYNAMIC_CLASS(wxApp
, wxEvtHandler
)
584 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
585 EVT_IDLE(wxApp::OnIdle
)
586 EVT_END_SESSION(wxApp::OnEndSession
)
587 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession
)
590 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
592 class wxCallBaseCleanup
595 wxCallBaseCleanup(wxApp
*app
) : m_app(app
) { }
596 ~wxCallBaseCleanup() { if ( m_app
) m_app
->wxAppBase::CleanUp(); }
598 void Dismiss() { m_app
= NULL
; }
605 bool wxApp::Initialize(int& argc
, wxChar
**argv
)
607 if ( !wxAppBase::Initialize(argc
, argv
) )
610 // ensure that base cleanup is done if we return too early
611 wxCallBaseCleanup
callBaseCleanup(this);
614 wxString tmp
= GetAppName();
615 tmp
+= wxT("ClassName");
616 wxCanvasClassName
= wxStrdup( tmp
.wc_str() );
618 wxCanvasClassNameNR
= wxStrdup( tmp
.wc_str() );
619 HWND hWnd
= FindWindow( wxCanvasClassNameNR
, NULL
);
622 SetForegroundWindow( (HWND
)(((DWORD
)hWnd
)|0x01) );
627 #if !defined(__WXMICROWIN__)
628 InitCommonControls();
629 #endif // !defined(__WXMICROWIN__)
631 #if defined(__SMARTPHONE__) || defined(__POCKETPC__)
632 SHInitExtraControls();
636 // Don't show a message box if a function such as SHGetFileInfo
637 // fails to find a device.
638 SetErrorMode(SEM_FAILCRITICALERRORS
|SEM_NOOPENFILEERRORBOX
);
643 RegisterWindowClasses();
645 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
646 wxSetKeyboardHook(true);
649 callBaseCleanup
.Dismiss();
654 // ---------------------------------------------------------------------------
655 // RegisterWindowClasses
656 // ---------------------------------------------------------------------------
658 // This function registers the given class name and stores a pointer to a
659 // heap-allocated copy of it at the specified location, it must be deleted
661 static void RegisterAndStoreClassName(const wxString
& uniqueClassName
,
662 const wxChar
**className
,
663 WNDCLASS
*lpWndClass
)
665 const size_t length
= uniqueClassName
.length() + 1; // for trailing NUL
666 wxChar
*newChars
= new wxChar
[length
];
667 wxStrncpy(newChars
, uniqueClassName
, length
);
668 *className
= newChars
;
669 lpWndClass
->lpszClassName
= *className
;
671 if ( !::RegisterClass(lpWndClass
) )
673 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"), newChars
));
677 // This function registers the class defined by the provided WNDCLASS struct
678 // contents using a unique name constructed from the specified base name and
679 // and a suffix unique to this library instance. It also stores the generated
680 // unique names for normal and "no redraw" versions of the class in the
681 // provided variables, caller must delete their contents later.
682 static void RegisterClassWithUniqueNames(const wxString
& baseName
,
683 const wxChar
**className
,
684 const wxChar
**classNameNR
,
685 WNDCLASS
*lpWndClass
)
687 // for each class we register one with CS_(V|H)REDRAW style and one
688 // without for windows created with wxNO_FULL_REDRAW_ON_REPAINT flag
689 static const long styleNormal
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
;
690 static const long styleNoRedraw
= CS_DBLCLKS
;
692 const wxString
uniqueSuffix(wxString::Format(wxT("@%p"), className
));
694 wxString
uniqueClassName(baseName
+ uniqueSuffix
);
695 lpWndClass
->style
= styleNormal
;
696 RegisterAndStoreClassName(uniqueClassName
, className
, lpWndClass
);
698 // NB: remember that code elsewhere supposes that no redraw class names
699 // use the same names as normal classes with "NR" suffix so we must put
700 // "NR" at the end instead of using more natural baseName+"NR"+suffix
701 wxString
uniqueClassNameNR(uniqueClassName
+ wxT("NR"));
702 lpWndClass
->style
= styleNoRedraw
;
703 RegisterAndStoreClassName(uniqueClassNameNR
, classNameNR
, lpWndClass
);
706 // TODO we should only register classes really used by the app. For this it
707 // would be enough to just delay the class registration until an attempt
708 // to create a window of this class is made.
709 bool wxApp::RegisterWindowClasses()
712 wxZeroMemory(wndclass
);
714 // the fields which are common to all classes
715 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
716 wndclass
.hInstance
= wxhInstance
;
717 wndclass
.hCursor
= ::LoadCursor((HINSTANCE
)NULL
, IDC_ARROW
);
719 // register the class for all normal windows and "no redraw" frames
720 wndclass
.hbrBackground
= (HBRUSH
)(COLOR_BTNFACE
+ 1);
721 RegisterClassWithUniqueNames(wxT("wxWindowClass"),
723 &wxCanvasClassNameNR
,
726 // Register the MDI frame window class and "no redraw" MDI frame
727 wndclass
.hbrBackground
= (HBRUSH
)NULL
; // paint MDI frame ourselves
728 RegisterClassWithUniqueNames(wxT("wxMDIFrameClass"),
729 &wxMDIFrameClassName
,
730 &wxMDIFrameClassNameNoRedraw
,
733 // Register the MDI child frame window class and "no redraw" MDI child frame
734 wndclass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
735 RegisterClassWithUniqueNames(wxT("wxMDIChildFrameClass"),
736 &wxMDIChildFrameClassName
,
737 &wxMDIChildFrameClassNameNoRedraw
,
743 // ---------------------------------------------------------------------------
744 // UnregisterWindowClasses
745 // ---------------------------------------------------------------------------
747 // This function unregisters the class with the given name and frees memory
748 // allocated for it by RegisterAndStoreClassName().
749 static bool UnregisterAndFreeClassName(const wxChar
**ppClassName
)
753 if ( !::UnregisterClass(*ppClassName
, wxhInstance
) )
756 wxString::Format(wxT("UnregisterClass(%s)"), *ppClassName
));
761 delete [] (wxChar
*) *ppClassName
;
767 bool wxApp::UnregisterWindowClasses()
771 #ifndef __WXMICROWIN__
772 if ( !UnregisterAndFreeClassName(&wxMDIFrameClassName
) )
775 if ( !UnregisterAndFreeClassName(&wxMDIFrameClassNameNoRedraw
) )
778 if ( !UnregisterAndFreeClassName(&wxMDIChildFrameClassName
) )
781 if ( !UnregisterAndFreeClassName(&wxMDIChildFrameClassNameNoRedraw
) )
784 if ( !UnregisterAndFreeClassName(&wxCanvasClassName
) )
787 if ( !UnregisterAndFreeClassName(&wxCanvasClassNameNR
) )
789 #endif // __WXMICROWIN__
794 void wxApp::CleanUp()
796 // all objects pending for deletion must be deleted first, otherwise
797 // UnregisterWindowClasses() call wouldn't succeed (because windows
798 // using the classes being unregistered still exist), so call the base
799 // class method first and only then do our clean up
800 wxAppBase::CleanUp();
802 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
803 wxSetKeyboardHook(false);
808 // for an EXE the classes are unregistered when it terminates but DLL may
809 // be loaded several times (load/unload/load) into the same process in
810 // which case the registration will fail after the first time if we don't
811 // unregister the classes now
812 UnregisterWindowClasses();
815 free( wxCanvasClassName
);
816 free( wxCanvasClassNameNR
);
820 // ----------------------------------------------------------------------------
822 // ----------------------------------------------------------------------------
826 m_printMode
= wxPRINT_WINDOWS
;
833 // ----------------------------------------------------------------------------
834 // wxApp idle handling
835 // ----------------------------------------------------------------------------
837 void wxApp::OnIdle(wxIdleEvent
& WXUNUSED(event
))
839 #if wxUSE_DC_CACHEING
840 // automated DC cache management: clear the cached DCs and bitmap
841 // if it's likely that the app has finished with them, that is, we
842 // get an idle event and we're not dragging anything.
843 if (!::GetKeyState(MK_LBUTTON
) && !::GetKeyState(MK_MBUTTON
) && !::GetKeyState(MK_RBUTTON
))
844 wxMSWDCImpl::ClearCache();
845 #endif // wxUSE_DC_CACHEING
848 void wxApp::WakeUpIdle()
850 // Send the top window a dummy message so idle handler processing will
851 // start up again. Doing it this way ensures that the idle handler
852 // wakes up in the right thread (see also wxWakeUpMainThread() which does
853 // the same for the main app thread only)
854 wxWindow
* const topWindow
= wxTheApp
->GetTopWindow();
857 HWND hwndTop
= GetHwndOf(topWindow
);
859 // Do not post WM_NULL if there's already a pending WM_NULL to avoid
860 // overflowing the message queue.
862 // Notice that due to a limitation of PeekMessage() API (which handles
863 // 0,0 range specially), we have to check the range from 0-1 instead.
864 // This still makes it possible to overflow the queue with WM_NULLs by
865 // interspersing the calles to WakeUpIdle() with windows creation but
866 // it should be rather hard to do it accidentally.
868 if ( !::PeekMessage(&msg
, hwndTop
, 0, 1, PM_NOREMOVE
) ||
869 ::PeekMessage(&msg
, hwndTop
, 1, 1, PM_NOREMOVE
) )
871 if ( !::PostMessage(hwndTop
, WM_NULL
, 0, 0) )
873 // should never happen
874 wxLogLastError(wxT("PostMessage(WM_NULL)"));
880 // ----------------------------------------------------------------------------
881 // other wxApp event hanlders
882 // ----------------------------------------------------------------------------
884 void wxApp::OnEndSession(wxCloseEvent
& WXUNUSED(event
))
886 // Windows will terminate the process soon after we return from
887 // WM_ENDSESSION handler or when we delete our last window, so make sure we
888 // at least execute our cleanup code before
890 // prevent the window from being destroyed when the corresponding wxTLW is
891 // destroyed: this will result in a leak of a HWND, of course, but who
892 // cares when the process is being killed anyhow
893 if ( !wxTopLevelWindows
.empty() )
894 wxTopLevelWindows
[0]->SetHWND(0);
896 const int rc
= OnExit();
900 // calling exit() instead of ExitProcess() or not doing anything at all and
901 // being killed by Windows has the advantage of executing the dtors of
906 // Default behaviour: close the application with prompts. The
907 // user can veto the close, and therefore the end session.
908 void wxApp::OnQueryEndSession(wxCloseEvent
& event
)
912 if (!GetTopWindow()->Close(!event
.CanVeto()))
917 // ----------------------------------------------------------------------------
918 // system DLL versions
919 // ----------------------------------------------------------------------------
921 // these functions have trivial inline implementations for CE
924 #if wxUSE_DYNLIB_CLASS
929 // helper function: retrieve the DLL version by using DllGetVersion(), returns
930 // 0 if the DLL doesn't export such function
931 int CallDllGetVersion(wxDynamicLibrary
& dll
)
933 // now check if the function is available during run-time
934 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC
, DllGetVersion
, dll
);
935 if ( !pfnDllGetVersion
)
939 dvi
.cbSize
= sizeof(dvi
);
941 HRESULT hr
= (*pfnDllGetVersion
)(&dvi
);
944 wxLogApiError(_T("DllGetVersion"), hr
);
949 return 100*dvi
.dwMajorVersion
+ dvi
.dwMinorVersion
;
952 } // anonymous namespace
955 int wxApp::GetComCtl32Version()
959 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
960 // but as its value should be the same both times it doesn't matter
961 static int s_verComCtl32
= -1;
963 if ( s_verComCtl32
== -1 )
965 // we're prepared to handle the errors
968 // the DLL should really be available
969 wxDynamicLibrary
dllComCtl32(_T("comctl32.dll"), wxDL_VERBATIM
);
970 if ( !dllComCtl32
.IsLoaded() )
976 // try DllGetVersion() for recent DLLs
977 s_verComCtl32
= CallDllGetVersion(dllComCtl32
);
979 // if DllGetVersion() is unavailable either during compile or
980 // run-time, try to guess the version otherwise
981 if ( !s_verComCtl32
)
983 // InitCommonControlsEx is unique to 4.70 and later
984 void *pfn
= dllComCtl32
.GetSymbol(_T("InitCommonControlsEx"));
987 // not found, must be 4.00
992 // many symbols appeared in comctl32 4.71, could use any of
993 // them except may be DllInstall()
994 pfn
= dllComCtl32
.GetSymbol(_T("InitializeFlatSB"));
997 // not found, must be 4.70
1002 // found, must be 4.71 or later
1003 s_verComCtl32
= 471;
1009 return s_verComCtl32
;
1013 int wxApp::GetShell32Version()
1015 static int s_verShell32
= -1;
1016 if ( s_verShell32
== -1 )
1018 // we're prepared to handle the errors
1021 wxDynamicLibrary
dllShell32(_T("shell32.dll"), wxDL_VERBATIM
);
1022 if ( dllShell32
.IsLoaded() )
1024 s_verShell32
= CallDllGetVersion(dllShell32
);
1026 if ( !s_verShell32
)
1028 // there doesn't seem to be any way to distinguish between 4.00
1029 // and 4.70 (starting from 4.71 we have DllGetVersion()) so
1030 // just assume it is 4.0
1034 else // failed load the DLL?
1040 return s_verShell32
;
1043 #else // !wxUSE_DYNLIB_CLASS
1046 int wxApp::GetComCtl32Version()
1052 int wxApp::GetShell32Version()
1057 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
1059 #endif // !__WXWINCE__
1061 // ----------------------------------------------------------------------------
1062 // Yield to incoming messages
1063 // ----------------------------------------------------------------------------
1065 bool wxApp::Yield(bool onlyIfNeeded
)
1068 static bool s_inYield
= false;
1071 // disable log flushing from here because a call to wxYield() shouldn't
1072 // normally result in message boxes popping up &c
1078 if ( !onlyIfNeeded
)
1080 wxFAIL_MSG( wxT("wxYield called recursively" ) );
1088 // we don't want to process WM_QUIT from here - it should be processed in
1089 // the main event loop in order to stop it
1090 wxEventLoopGuarantor dummyLoopIfNeeded
;
1092 while ( PeekMessage(&msg
, (HWND
)0, 0, 0, PM_NOREMOVE
) &&
1093 msg
.message
!= WM_QUIT
)
1096 wxMutexGuiLeaveOrEnter();
1097 #endif // wxUSE_THREADS
1099 if ( !wxTheApp
->Dispatch() )
1103 // if there are pending events, we must process them.
1104 ProcessPendingEvents();
1107 // let the logs be flashed again
1116 #if wxUSE_EXCEPTIONS
1118 // ----------------------------------------------------------------------------
1119 // exception handling
1120 // ----------------------------------------------------------------------------
1122 bool wxApp::OnExceptionInMainLoop()
1124 // ask the user about what to do: use the Win32 API function here as it
1125 // could be dangerous to use any wxWidgets code in this state
1130 _T("An unhandled exception occurred. Press \"Abort\" to \
1131 terminate the program,\r\n\
1132 \"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
1133 _T("Unhandled exception"),
1134 MB_ABORTRETRYIGNORE
|
1144 wxFAIL_MSG( _T("unexpected MessageBox() return code") );
1155 #endif // wxUSE_EXCEPTIONS