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/ole/oleutils.h"
56 #include "wx/msw/private/timer.h"
59 #include "wx/tooltip.h"
60 #endif // wxUSE_TOOLTIPS
62 // OLE is used for drag-and-drop, clipboard, OLE Automation..., but some
63 // compilers don't support it (missing headers, libs, ...)
64 #if defined(__GNUWIN32_OLD__) || defined(__SYMANTEC__) || defined(__SALFORDC__)
68 #endif // broken compilers
70 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
82 #include "wx/msw/missing.h"
84 // instead of including <shlwapi.h> which is not part of the core SDK and not
85 // shipped at all with other compilers, we always define the parts of it we
86 // need here ourselves
88 // NB: DLLVER_PLATFORM_WINDOWS will be defined if shlwapi.h had been somehow
90 #ifndef DLLVER_PLATFORM_WINDOWS
91 // hopefully we don't need to change packing as DWORDs should be already
96 DWORD dwMajorVersion
; // Major version
97 DWORD dwMinorVersion
; // Minor version
98 DWORD dwBuildNumber
; // Build number
99 DWORD dwPlatformID
; // DLLVER_PLATFORM_*
102 typedef HRESULT (CALLBACK
* DLLGETVERSIONPROC
)(DLLVERSIONINFO
*);
103 #endif // defined(DLLVERSIONINFO)
106 // ---------------------------------------------------------------------------
108 // ---------------------------------------------------------------------------
110 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
111 extern void wxSetKeyboardHook(bool doIt
);
114 // NB: all "NoRedraw" classes must have the same names as the "normal" classes
115 // with NR suffix - wxWindow::MSWCreate() supposes this
117 WXDLLIMPEXP_CORE wxChar
*wxCanvasClassName
;
118 WXDLLIMPEXP_CORE wxChar
*wxCanvasClassNameNR
;
120 WXDLLIMPEXP_CORE
const wxChar
*wxCanvasClassName
= wxT("wxWindowClass");
121 WXDLLIMPEXP_CORE
const wxChar
*wxCanvasClassNameNR
= wxT("wxWindowClassNR");
123 WXDLLIMPEXP_CORE
const wxChar
*wxMDIFrameClassName
= wxT("wxMDIFrameClass");
124 WXDLLIMPEXP_CORE
const wxChar
*wxMDIFrameClassNameNoRedraw
= wxT("wxMDIFrameClassNR");
125 WXDLLIMPEXP_CORE
const wxChar
*wxMDIChildFrameClassName
= wxT("wxMDIChildFrameClass");
126 WXDLLIMPEXP_CORE
const wxChar
*wxMDIChildFrameClassNameNoRedraw
= wxT("wxMDIChildFrameClassNR");
128 // ----------------------------------------------------------------------------
130 // ----------------------------------------------------------------------------
132 LRESULT WXDLLEXPORT APIENTRY
wxWndProc(HWND
, UINT
, WPARAM
, LPARAM
);
134 // ===========================================================================
135 // wxGUIAppTraits implementation
136 // ===========================================================================
138 // private class which we use to pass parameters from BeforeChildWaitLoop() to
139 // AfterChildWaitLoop()
140 struct ChildWaitLoopData
142 ChildWaitLoopData(wxWindowDisabler
*wd_
, wxWindow
*winActive_
)
145 winActive
= winActive_
;
148 wxWindowDisabler
*wd
;
152 void *wxGUIAppTraits::BeforeChildWaitLoop()
155 We use a dirty hack here to disable all application windows (which we
156 must do because otherwise the calls to wxYield() could lead to some very
157 unexpected reentrancies in the users code) but to avoid losing
158 focus/activation entirely when the child process terminates which would
159 happen if we simply disabled everything using wxWindowDisabler. Indeed,
160 remember that Windows will never activate a disabled window and when the
161 last childs window is closed and Windows looks for a window to activate
162 all our windows are still disabled. There is no way to enable them in
163 time because we don't know when the childs windows are going to be
164 closed, so the solution we use here is to keep one special tiny frame
165 enabled all the time. Then when the child terminates it will get
166 activated and when we close it below -- after reenabling all the other
167 windows! -- the previously active window becomes activated again and
172 // first disable all existing windows
173 wxWindowDisabler
*wd
= new wxWindowDisabler
;
175 // then create an "invisible" frame: it has minimal size, is positioned
176 // (hopefully) outside the screen and doesn't appear on the taskbar
177 wxWindow
*winActive
= new wxFrame
179 wxTheApp
->GetTopWindow(),
182 wxPoint(32600, 32600),
184 wxDEFAULT_FRAME_STYLE
| wxFRAME_NO_TASKBAR
188 return new ChildWaitLoopData(wd
, winActive
);
191 void wxGUIAppTraits::AlwaysYield()
196 void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig
)
200 ChildWaitLoopData
* const data
= (ChildWaitLoopData
*)dataOrig
;
204 // finally delete the dummy frame and, as wd has been already destroyed and
205 // the other windows reenabled, the activation is going to return to the
206 // window which had had it before
207 data
->winActive
->Destroy();
209 // also delete the temporary data object itself
213 bool wxGUIAppTraits::DoMessageFromThreadWait()
215 // we should return false only if the app should exit, i.e. only if
216 // Dispatch() determines that the main event loop should terminate
217 wxEventLoopBase
* const evtLoop
= wxEventLoop::GetActive();
218 if ( !evtLoop
|| !evtLoop
->Pending() )
220 // no events means no quit event
224 return evtLoop
->Dispatch();
227 DWORD
wxGUIAppTraits::WaitForThread(WXHANDLE hThread
)
229 // if we don't have a running event loop, we shouldn't wait for the
230 // messages as we never remove them from the message queue and so we enter
231 // an infinite loop as MsgWaitForMultipleObjects() keeps returning
233 if ( !wxEventLoop::GetActive() )
234 return DoSimpleWaitForThread(hThread
);
236 return ::MsgWaitForMultipleObjects
238 1, // number of objects to wait for
239 (HANDLE
*)&hThread
, // the objects
240 false, // wait for any objects, not all
241 INFINITE
, // no timeout
242 QS_ALLINPUT
| // return as soon as there are any events
247 wxPortId
wxGUIAppTraits::GetToolkitVersion(int *majVer
, int *minVer
) const
252 // on Windows, the toolkit version is the same of the OS version
253 // as Windows integrates the OS kernel with the GUI toolkit.
254 info
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
255 if ( ::GetVersionEx(&info
) )
258 *majVer
= info
.dwMajorVersion
;
260 *minVer
= info
.dwMinorVersion
;
263 #if defined(__WXHANDHELD__) || defined(__WXWINCE__)
272 wxTimerImpl
*wxGUIAppTraits::CreateTimerImpl(wxTimer
*timer
)
274 return new wxMSWTimerImpl(timer
);
277 #endif // wxUSE_TIMER
279 wxEventLoopBase
* wxGUIAppTraits::CreateEventLoop()
281 return new wxEventLoop
;
284 // ===========================================================================
285 // wxApp implementation
286 // ===========================================================================
288 int wxApp::m_nCmdShow
= SW_SHOWNORMAL
;
290 // ---------------------------------------------------------------------------
292 // ---------------------------------------------------------------------------
294 IMPLEMENT_DYNAMIC_CLASS(wxApp
, wxEvtHandler
)
296 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
297 EVT_IDLE(wxApp::OnIdle
)
298 EVT_END_SESSION(wxApp::OnEndSession
)
299 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession
)
302 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
304 class wxCallBaseCleanup
307 wxCallBaseCleanup(wxApp
*app
) : m_app(app
) { }
308 ~wxCallBaseCleanup() { if ( m_app
) m_app
->wxAppBase::CleanUp(); }
310 void Dismiss() { m_app
= NULL
; }
317 bool wxApp::Initialize(int& argc
, wxChar
**argv
)
319 if ( !wxAppBase::Initialize(argc
, argv
) )
322 // ensure that base cleanup is done if we return too early
323 wxCallBaseCleanup
callBaseCleanup(this);
326 wxString tmp
= GetAppName();
327 tmp
+= wxT("ClassName");
328 wxCanvasClassName
= wxStrdup( tmp
.wc_str() );
330 wxCanvasClassNameNR
= wxStrdup( tmp
.wc_str() );
331 HWND hWnd
= FindWindow( wxCanvasClassNameNR
, NULL
);
334 SetForegroundWindow( (HWND
)(((DWORD
)hWnd
)|0x01) );
339 #if !defined(__WXMICROWIN__)
340 InitCommonControls();
341 #endif // !defined(__WXMICROWIN__)
343 #if defined(__SMARTPHONE__) || defined(__POCKETPC__)
344 SHInitExtraControls();
348 // Don't show a message box if a function such as SHGetFileInfo
349 // fails to find a device.
350 SetErrorMode(SEM_FAILCRITICALERRORS
|SEM_NOOPENFILEERRORBOX
);
355 RegisterWindowClasses();
357 wxWinHandleHash
= new wxWinHashTable(wxKEY_INTEGER
, 100);
359 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
360 wxSetKeyboardHook(true);
363 callBaseCleanup
.Dismiss();
368 // ---------------------------------------------------------------------------
369 // RegisterWindowClasses
370 // ---------------------------------------------------------------------------
372 // TODO we should only register classes really used by the app. For this it
373 // would be enough to just delay the class registration until an attempt
374 // to create a window of this class is made.
375 bool wxApp::RegisterWindowClasses()
378 wxZeroMemory(wndclass
);
380 // for each class we register one with CS_(V|H)REDRAW style and one
381 // without for windows created with wxNO_FULL_REDRAW_ON_REPAINT flag
382 static const long styleNormal
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
;
383 static const long styleNoRedraw
= CS_DBLCLKS
;
385 // the fields which are common to all classes
386 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
387 wndclass
.hInstance
= wxhInstance
;
388 wndclass
.hCursor
= ::LoadCursor((HINSTANCE
)NULL
, IDC_ARROW
);
390 // register the class for all normal windows
391 wndclass
.hbrBackground
= (HBRUSH
)(COLOR_BTNFACE
+ 1);
392 wndclass
.lpszClassName
= wxCanvasClassName
;
393 wndclass
.style
= styleNormal
;
395 if ( !RegisterClass(&wndclass
) )
397 wxLogLastError(wxT("RegisterClass(frame)"));
401 wndclass
.lpszClassName
= wxCanvasClassNameNR
;
402 wndclass
.style
= styleNoRedraw
;
404 if ( !RegisterClass(&wndclass
) )
406 wxLogLastError(wxT("RegisterClass(no redraw frame)"));
409 // Register the MDI frame window class.
410 wndclass
.hbrBackground
= (HBRUSH
)NULL
; // paint MDI frame ourselves
411 wndclass
.lpszClassName
= wxMDIFrameClassName
;
412 wndclass
.style
= styleNormal
;
414 if ( !RegisterClass(&wndclass
) )
416 wxLogLastError(wxT("RegisterClass(MDI parent)"));
419 // "no redraw" MDI frame
420 wndclass
.lpszClassName
= wxMDIFrameClassNameNoRedraw
;
421 wndclass
.style
= styleNoRedraw
;
423 if ( !RegisterClass(&wndclass
) )
425 wxLogLastError(wxT("RegisterClass(no redraw MDI parent frame)"));
428 // Register the MDI child frame window class.
429 wndclass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
430 wndclass
.lpszClassName
= wxMDIChildFrameClassName
;
431 wndclass
.style
= styleNormal
;
433 if ( !RegisterClass(&wndclass
) )
435 wxLogLastError(wxT("RegisterClass(MDI child)"));
438 // "no redraw" MDI child frame
439 wndclass
.lpszClassName
= wxMDIChildFrameClassNameNoRedraw
;
440 wndclass
.style
= styleNoRedraw
;
442 if ( !RegisterClass(&wndclass
) )
444 wxLogLastError(wxT("RegisterClass(no redraw MDI child)"));
450 // ---------------------------------------------------------------------------
451 // UnregisterWindowClasses
452 // ---------------------------------------------------------------------------
454 bool wxApp::UnregisterWindowClasses()
458 #ifndef __WXMICROWIN__
459 // MDI frame window class.
460 if ( !::UnregisterClass(wxMDIFrameClassName
, wxhInstance
) )
462 wxLogLastError(wxT("UnregisterClass(MDI parent)"));
467 // "no redraw" MDI frame
468 if ( !::UnregisterClass(wxMDIFrameClassNameNoRedraw
, wxhInstance
) )
470 wxLogLastError(wxT("UnregisterClass(no redraw MDI parent frame)"));
475 // MDI child frame window class.
476 if ( !::UnregisterClass(wxMDIChildFrameClassName
, wxhInstance
) )
478 wxLogLastError(wxT("UnregisterClass(MDI child)"));
483 // "no redraw" MDI child frame
484 if ( !::UnregisterClass(wxMDIChildFrameClassNameNoRedraw
, wxhInstance
) )
486 wxLogLastError(wxT("UnregisterClass(no redraw MDI child)"));
492 if ( !::UnregisterClass(wxCanvasClassName
, wxhInstance
) )
494 wxLogLastError(wxT("UnregisterClass(canvas)"));
499 if ( !::UnregisterClass(wxCanvasClassNameNR
, wxhInstance
) )
501 wxLogLastError(wxT("UnregisterClass(no redraw canvas)"));
505 #endif // __WXMICROWIN__
510 void wxApp::CleanUp()
512 // all objects pending for deletion must be deleted first, otherwise we
513 // would crash when they use wxWinHandleHash (and UnregisterWindowClasses()
514 // call wouldn't succeed as long as any windows still exist), so call the
515 // base class method first and only then do our clean up
516 wxAppBase::CleanUp();
518 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
519 wxSetKeyboardHook(false);
524 // for an EXE the classes are unregistered when it terminates but DLL may
525 // be loaded several times (load/unload/load) into the same process in
526 // which case the registration will fail after the first time if we don't
527 // unregister the classes now
528 UnregisterWindowClasses();
530 delete wxWinHandleHash
;
531 wxWinHandleHash
= NULL
;
534 free( wxCanvasClassName
);
535 free( wxCanvasClassNameNR
);
539 // ----------------------------------------------------------------------------
541 // ----------------------------------------------------------------------------
545 m_printMode
= wxPRINT_WINDOWS
;
552 // ----------------------------------------------------------------------------
553 // wxApp idle handling
554 // ----------------------------------------------------------------------------
556 void wxApp::OnIdle(wxIdleEvent
& WXUNUSED(event
))
558 #if wxUSE_DC_CACHEING
559 // automated DC cache management: clear the cached DCs and bitmap
560 // if it's likely that the app has finished with them, that is, we
561 // get an idle event and we're not dragging anything.
562 if (!::GetKeyState(MK_LBUTTON
) && !::GetKeyState(MK_MBUTTON
) && !::GetKeyState(MK_RBUTTON
))
563 wxMSWDCImpl::ClearCache();
564 #endif // wxUSE_DC_CACHEING
567 void wxApp::WakeUpIdle()
569 // Send the top window a dummy message so idle handler processing will
570 // start up again. Doing it this way ensures that the idle handler
571 // wakes up in the right thread (see also wxWakeUpMainThread() which does
572 // the same for the main app thread only)
573 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
576 if ( !::PostMessage(GetHwndOf(topWindow
), WM_NULL
, 0, 0) )
578 // should never happen
579 wxLogLastError(wxT("PostMessage(WM_NULL)"));
584 // ----------------------------------------------------------------------------
585 // other wxApp event hanlders
586 // ----------------------------------------------------------------------------
588 void wxApp::OnEndSession(wxCloseEvent
& WXUNUSED(event
))
591 GetTopWindow()->Close(true);
594 // Default behaviour: close the application with prompts. The
595 // user can veto the close, and therefore the end session.
596 void wxApp::OnQueryEndSession(wxCloseEvent
& event
)
600 if (!GetTopWindow()->Close(!event
.CanVeto()))
605 // ----------------------------------------------------------------------------
606 // system DLL versions
607 // ----------------------------------------------------------------------------
609 #if wxUSE_DYNLIB_CLASS
614 // helper function: retrieve the DLL version by using DllGetVersion(), returns
615 // 0 if the DLL doesn't export such function
616 int CallDllGetVersion(wxDynamicLibrary
& dll
)
618 // now check if the function is available during run-time
619 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC
, DllGetVersion
, dll
);
620 if ( !pfnDllGetVersion
)
624 dvi
.cbSize
= sizeof(dvi
);
626 HRESULT hr
= (*pfnDllGetVersion
)(&dvi
);
629 wxLogApiError(_T("DllGetVersion"), hr
);
634 return 100*dvi
.dwMajorVersion
+ dvi
.dwMinorVersion
;
637 } // anonymous namespace
640 int wxApp::GetComCtl32Version()
644 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
645 // but as its value should be the same both times it doesn't matter
646 static int s_verComCtl32
= -1;
648 if ( s_verComCtl32
== -1 )
650 // we're prepared to handle the errors
653 // the DLL should really be available
654 wxDynamicLibrary
dllComCtl32(_T("comctl32.dll"), wxDL_VERBATIM
);
655 if ( !dllComCtl32
.IsLoaded() )
661 // try DllGetVersion() for recent DLLs
662 s_verComCtl32
= CallDllGetVersion(dllComCtl32
);
664 // if DllGetVersion() is unavailable either during compile or
665 // run-time, try to guess the version otherwise
666 if ( !s_verComCtl32
)
668 // InitCommonControlsEx is unique to 4.70 and later
669 void *pfn
= dllComCtl32
.GetSymbol(_T("InitCommonControlsEx"));
672 // not found, must be 4.00
677 // many symbols appeared in comctl32 4.71, could use any of
678 // them except may be DllInstall()
679 pfn
= dllComCtl32
.GetSymbol(_T("InitializeFlatSB"));
682 // not found, must be 4.70
687 // found, must be 4.71 or later
694 return s_verComCtl32
;
698 int wxApp::GetShell32Version()
700 static int s_verShell32
= -1;
701 if ( s_verShell32
== -1 )
703 // we're prepared to handle the errors
706 wxDynamicLibrary
dllShell32(_T("shell32.dll"), wxDL_VERBATIM
);
707 if ( dllShell32
.IsLoaded() )
709 s_verShell32
= CallDllGetVersion(dllShell32
);
713 // there doesn't seem to be any way to distinguish between 4.00
714 // and 4.70 (starting from 4.71 we have DllGetVersion()) so
715 // just assume it is 4.0
719 else // failed load the DLL?
728 #else // !wxUSE_DYNLIB_CLASS
731 int wxApp::GetComCtl32Version()
737 int wxApp::GetShell32Version()
742 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
744 // ----------------------------------------------------------------------------
745 // Yield to incoming messages
746 // ----------------------------------------------------------------------------
748 bool wxApp::Yield(bool onlyIfNeeded
)
751 static bool s_inYield
= false;
754 // disable log flushing from here because a call to wxYield() shouldn't
755 // normally result in message boxes popping up &c
763 wxFAIL_MSG( wxT("wxYield called recursively" ) );
771 // we don't want to process WM_QUIT from here - it should be processed in
772 // the main event loop in order to stop it
774 while ( PeekMessage(&msg
, (HWND
)0, 0, 0, PM_NOREMOVE
) &&
775 msg
.message
!= WM_QUIT
)
778 wxMutexGuiLeaveOrEnter();
779 #endif // wxUSE_THREADS
781 if ( !wxTheApp
->Dispatch() )
785 // if there are pending events, we must process them.
786 ProcessPendingEvents();
789 // let the logs be flashed again
800 // ----------------------------------------------------------------------------
801 // exception handling
802 // ----------------------------------------------------------------------------
804 bool wxApp::OnExceptionInMainLoop()
806 // ask the user about what to do: use the Win32 API function here as it
807 // could be dangerous to use any wxWidgets code in this state
812 _T("An unhandled exception occurred. Press \"Abort\" to \
813 terminate the program,\r\n\
814 \"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
815 _T("Unhandled exception"),
816 MB_ABORTRETRYIGNORE
|
826 wxFAIL_MSG( _T("unexpected MessageBox() return code") );
837 #endif // wxUSE_EXCEPTIONS