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"
43 #include "wx/wxchar.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"
53 #include "wx/msw/private.h"
54 #include "wx/msw/ole/oleutils.h"
57 #include "wx/tooltip.h"
58 #endif // wxUSE_TOOLTIPS
60 // OLE is used for drag-and-drop, clipboard, OLE Automation..., but some
61 // compilers don't support it (missing headers, libs, ...)
62 #if defined(__GNUWIN32_OLD__) || defined(__SYMANTEC__) || defined(__SALFORDC__)
66 #endif // broken compilers
68 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
80 #include "wx/msw/missing.h"
82 // instead of including <shlwapi.h> which is not part of the core SDK and not
83 // shipped at all with other compilers, we always define the parts of it we
84 // need here ourselves
86 // NB: DLLVER_PLATFORM_WINDOWS will be defined if shlwapi.h had been somehow
88 #ifndef DLLVER_PLATFORM_WINDOWS
89 // hopefully we don't need to change packing as DWORDs should be already
94 DWORD dwMajorVersion
; // Major version
95 DWORD dwMinorVersion
; // Minor version
96 DWORD dwBuildNumber
; // Build number
97 DWORD dwPlatformID
; // DLLVER_PLATFORM_*
100 typedef HRESULT (CALLBACK
* DLLGETVERSIONPROC
)(DLLVERSIONINFO
*);
101 #endif // defined(DLLVERSIONINFO)
104 // ---------------------------------------------------------------------------
106 // ---------------------------------------------------------------------------
108 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
109 extern void wxSetKeyboardHook(bool doIt
);
112 // NB: all "NoRedraw" classes must have the same names as the "normal" classes
113 // with NR suffix - wxWindow::MSWCreate() supposes this
115 WXDLLIMPEXP_CORE wxChar
*wxCanvasClassName
;
116 WXDLLIMPEXP_CORE wxChar
*wxCanvasClassNameNR
;
118 WXDLLIMPEXP_CORE
const wxChar
*wxCanvasClassName
= wxT("wxWindowClass");
119 WXDLLIMPEXP_CORE
const wxChar
*wxCanvasClassNameNR
= wxT("wxWindowClassNR");
121 WXDLLIMPEXP_CORE
const wxChar
*wxMDIFrameClassName
= wxT("wxMDIFrameClass");
122 WXDLLIMPEXP_CORE
const wxChar
*wxMDIFrameClassNameNoRedraw
= wxT("wxMDIFrameClassNR");
123 WXDLLIMPEXP_CORE
const wxChar
*wxMDIChildFrameClassName
= wxT("wxMDIChildFrameClass");
124 WXDLLIMPEXP_CORE
const wxChar
*wxMDIChildFrameClassNameNoRedraw
= wxT("wxMDIChildFrameClassNR");
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 wxEventLoop
*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__)
268 // ===========================================================================
269 // wxApp implementation
270 // ===========================================================================
272 int wxApp::m_nCmdShow
= SW_SHOWNORMAL
;
274 // ---------------------------------------------------------------------------
276 // ---------------------------------------------------------------------------
278 IMPLEMENT_DYNAMIC_CLASS(wxApp
, wxEvtHandler
)
280 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
281 EVT_IDLE(wxApp::OnIdle
)
282 EVT_END_SESSION(wxApp::OnEndSession
)
283 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession
)
286 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
288 class wxCallBaseCleanup
291 wxCallBaseCleanup(wxApp
*app
) : m_app(app
) { }
292 ~wxCallBaseCleanup() { if ( m_app
) m_app
->wxAppBase::CleanUp(); }
294 void Dismiss() { m_app
= NULL
; }
301 bool wxApp::Initialize(int& argc
, wxChar
**argv
)
303 if ( !wxAppBase::Initialize(argc
, argv
) )
306 // ensure that base cleanup is done if we return too early
307 wxCallBaseCleanup
callBaseCleanup(this);
310 wxString tmp
= GetAppName();
311 tmp
+= wxT("ClassName");
312 wxCanvasClassName
= wxStrdup( tmp
.c_str() );
314 wxCanvasClassNameNR
= wxStrdup( tmp
.c_str() );
315 HWND hWnd
= FindWindow( wxCanvasClassNameNR
, NULL
);
318 SetForegroundWindow( (HWND
)(((DWORD
)hWnd
)|0x01) );
323 #if !defined(__WXMICROWIN__)
324 InitCommonControls();
325 #endif // !defined(__WXMICROWIN__)
327 #if defined(__SMARTPHONE__) || defined(__POCKETPC__)
328 SHInitExtraControls();
332 // Don't show a message box if a function such as SHGetFileInfo
333 // fails to find a device.
334 SetErrorMode(SEM_FAILCRITICALERRORS
|SEM_NOOPENFILEERRORBOX
);
339 RegisterWindowClasses();
341 wxWinHandleHash
= new wxWinHashTable(wxKEY_INTEGER
, 100);
343 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
344 wxSetKeyboardHook(true);
347 callBaseCleanup
.Dismiss();
352 // ---------------------------------------------------------------------------
353 // RegisterWindowClasses
354 // ---------------------------------------------------------------------------
356 // TODO we should only register classes really used by the app. For this it
357 // would be enough to just delay the class registration until an attempt
358 // to create a window of this class is made.
359 bool wxApp::RegisterWindowClasses()
362 wxZeroMemory(wndclass
);
364 // for each class we register one with CS_(V|H)REDRAW style and one
365 // without for windows created with wxNO_FULL_REDRAW_ON_REPAINT flag
366 static const long styleNormal
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
;
367 static const long styleNoRedraw
= CS_DBLCLKS
;
369 // the fields which are common to all classes
370 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
371 wndclass
.hInstance
= wxhInstance
;
372 wndclass
.hCursor
= ::LoadCursor((HINSTANCE
)NULL
, IDC_ARROW
);
374 // register the class for all normal windows
375 wndclass
.hbrBackground
= (HBRUSH
)(COLOR_BTNFACE
+ 1);
376 wndclass
.lpszClassName
= wxCanvasClassName
;
377 wndclass
.style
= styleNormal
;
379 if ( !RegisterClass(&wndclass
) )
381 wxLogLastError(wxT("RegisterClass(frame)"));
385 wndclass
.lpszClassName
= wxCanvasClassNameNR
;
386 wndclass
.style
= styleNoRedraw
;
388 if ( !RegisterClass(&wndclass
) )
390 wxLogLastError(wxT("RegisterClass(no redraw frame)"));
393 // Register the MDI frame window class.
394 wndclass
.hbrBackground
= (HBRUSH
)NULL
; // paint MDI frame ourselves
395 wndclass
.lpszClassName
= wxMDIFrameClassName
;
396 wndclass
.style
= styleNormal
;
398 if ( !RegisterClass(&wndclass
) )
400 wxLogLastError(wxT("RegisterClass(MDI parent)"));
403 // "no redraw" MDI frame
404 wndclass
.lpszClassName
= wxMDIFrameClassNameNoRedraw
;
405 wndclass
.style
= styleNoRedraw
;
407 if ( !RegisterClass(&wndclass
) )
409 wxLogLastError(wxT("RegisterClass(no redraw MDI parent frame)"));
412 // Register the MDI child frame window class.
413 wndclass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
414 wndclass
.lpszClassName
= wxMDIChildFrameClassName
;
415 wndclass
.style
= styleNormal
;
417 if ( !RegisterClass(&wndclass
) )
419 wxLogLastError(wxT("RegisterClass(MDI child)"));
422 // "no redraw" MDI child frame
423 wndclass
.lpszClassName
= wxMDIChildFrameClassNameNoRedraw
;
424 wndclass
.style
= styleNoRedraw
;
426 if ( !RegisterClass(&wndclass
) )
428 wxLogLastError(wxT("RegisterClass(no redraw MDI child)"));
434 // ---------------------------------------------------------------------------
435 // UnregisterWindowClasses
436 // ---------------------------------------------------------------------------
438 bool wxApp::UnregisterWindowClasses()
442 #ifndef __WXMICROWIN__
443 // MDI frame window class.
444 if ( !::UnregisterClass(wxMDIFrameClassName
, wxhInstance
) )
446 wxLogLastError(wxT("UnregisterClass(MDI parent)"));
451 // "no redraw" MDI frame
452 if ( !::UnregisterClass(wxMDIFrameClassNameNoRedraw
, wxhInstance
) )
454 wxLogLastError(wxT("UnregisterClass(no redraw MDI parent frame)"));
459 // MDI child frame window class.
460 if ( !::UnregisterClass(wxMDIChildFrameClassName
, wxhInstance
) )
462 wxLogLastError(wxT("UnregisterClass(MDI child)"));
467 // "no redraw" MDI child frame
468 if ( !::UnregisterClass(wxMDIChildFrameClassNameNoRedraw
, wxhInstance
) )
470 wxLogLastError(wxT("UnregisterClass(no redraw MDI child)"));
476 if ( !::UnregisterClass(wxCanvasClassName
, wxhInstance
) )
478 wxLogLastError(wxT("UnregisterClass(canvas)"));
483 if ( !::UnregisterClass(wxCanvasClassNameNR
, wxhInstance
) )
485 wxLogLastError(wxT("UnregisterClass(no redraw canvas)"));
489 #endif // __WXMICROWIN__
494 void wxApp::CleanUp()
496 // all objects pending for deletion must be deleted first, otherwise we
497 // would crash when they use wxWinHandleHash (and UnregisterWindowClasses()
498 // call wouldn't succeed as long as any windows still exist), so call the
499 // base class method first and only then do our clean up
500 wxAppBase::CleanUp();
502 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
503 wxSetKeyboardHook(false);
508 // for an EXE the classes are unregistered when it terminates but DLL may
509 // be loaded several times (load/unload/load) into the same process in
510 // which case the registration will fail after the first time if we don't
511 // unregister the classes now
512 UnregisterWindowClasses();
514 delete wxWinHandleHash
;
515 wxWinHandleHash
= NULL
;
518 free( wxCanvasClassName
);
519 free( wxCanvasClassNameNR
);
523 // ----------------------------------------------------------------------------
525 // ----------------------------------------------------------------------------
529 m_printMode
= wxPRINT_WINDOWS
;
536 // ----------------------------------------------------------------------------
537 // wxApp idle handling
538 // ----------------------------------------------------------------------------
540 void wxApp::OnIdle(wxIdleEvent
& event
)
542 wxAppBase::OnIdle(event
);
544 #if wxUSE_DC_CACHEING
545 // automated DC cache management: clear the cached DCs and bitmap
546 // if it's likely that the app has finished with them, that is, we
547 // get an idle event and we're not dragging anything.
548 if (!::GetKeyState(MK_LBUTTON
) && !::GetKeyState(MK_MBUTTON
) && !::GetKeyState(MK_RBUTTON
))
550 #endif // wxUSE_DC_CACHEING
553 void wxApp::WakeUpIdle()
555 // Send the top window a dummy message so idle handler processing will
556 // start up again. Doing it this way ensures that the idle handler
557 // wakes up in the right thread (see also wxWakeUpMainThread() which does
558 // the same for the main app thread only)
559 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
562 if ( !::PostMessage(GetHwndOf(topWindow
), WM_NULL
, 0, 0) )
564 // should never happen
565 wxLogLastError(wxT("PostMessage(WM_NULL)"));
570 // ----------------------------------------------------------------------------
571 // other wxApp event hanlders
572 // ----------------------------------------------------------------------------
574 void wxApp::OnEndSession(wxCloseEvent
& WXUNUSED(event
))
577 GetTopWindow()->Close(true);
580 // Default behaviour: close the application with prompts. The
581 // user can veto the close, and therefore the end session.
582 void wxApp::OnQueryEndSession(wxCloseEvent
& event
)
586 if (!GetTopWindow()->Close(!event
.CanVeto()))
591 // ----------------------------------------------------------------------------
593 // ----------------------------------------------------------------------------
596 int wxApp::GetComCtl32Version()
598 #if defined(__WXMICROWIN__) || defined(__WXWINCE__)
603 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
604 // but as its value should be the same both times it doesn't matter
605 static int s_verComCtl32
= -1;
607 if ( s_verComCtl32
== -1 )
609 // initally assume no comctl32.dll at all
612 // we're prepared to handle the errors
615 #if wxUSE_DYNLIB_CLASS
617 wxDynamicLibrary
dllComCtl32(_T("comctl32.dll"), wxDL_VERBATIM
);
619 // if so, then we can check for the version
620 if ( dllComCtl32
.IsLoaded() )
622 // now check if the function is available during run-time
623 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC
, DllGetVersion
, dllComCtl32
);
624 if ( pfnDllGetVersion
)
627 dvi
.cbSize
= sizeof(dvi
);
629 HRESULT hr
= (*pfnDllGetVersion
)(&dvi
);
632 wxLogApiError(_T("DllGetVersion"), hr
);
636 // this is incompatible with _WIN32_IE values, but
637 // compatible with the other values returned by
638 // GetComCtl32Version()
639 s_verComCtl32
= 100*dvi
.dwMajorVersion
+
644 // if DllGetVersion() is unavailable either during compile or
645 // run-time, try to guess the version otherwise
646 if ( !s_verComCtl32
)
648 // InitCommonControlsEx is unique to 4.70 and later
649 void *pfn
= dllComCtl32
.GetSymbol(_T("InitCommonControlsEx"));
652 // not found, must be 4.00
657 // many symbols appeared in comctl32 4.71, could use any of
658 // them except may be DllInstall()
659 pfn
= dllComCtl32
.GetSymbol(_T("InitializeFlatSB"));
662 // not found, must be 4.70
667 // found, must be 4.71 or later
676 return s_verComCtl32
;
677 #endif // Microwin/!Microwin
680 // Yield to incoming messages
682 bool wxApp::Yield(bool onlyIfNeeded
)
685 static bool s_inYield
= false;
688 // disable log flushing from here because a call to wxYield() shouldn't
689 // normally result in message boxes popping up &c
697 wxFAIL_MSG( wxT("wxYield called recursively" ) );
705 // we don't want to process WM_QUIT from here - it should be processed in
706 // the main event loop in order to stop it
708 while ( PeekMessage(&msg
, (HWND
)0, 0, 0, PM_NOREMOVE
) &&
709 msg
.message
!= WM_QUIT
)
712 wxMutexGuiLeaveOrEnter();
713 #endif // wxUSE_THREADS
715 if ( !wxTheApp
->Dispatch() )
719 // if there are pending events, we must process them.
720 ProcessPendingEvents();
723 // let the logs be flashed again
734 // ----------------------------------------------------------------------------
735 // exception handling
736 // ----------------------------------------------------------------------------
738 bool wxApp::OnExceptionInMainLoop()
740 // ask the user about what to do: use the Win32 API function here as it
741 // could be dangerous to use any wxWidgets code in this state
746 _T("An unhandled exception occurred. Press \"Abort\" to \
747 terminate the program,\r\n\
748 \"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
749 _T("Unhandled exception"),
750 MB_ABORTRETRYIGNORE
|
760 wxFAIL_MSG( _T("unexpected MessageBox() return code") );
771 #endif // wxUSE_EXCEPTIONS