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/dynarray.h"
32 #include "wx/gdicmn.h"
35 #include "wx/cursor.h"
37 #include "wx/palette.h"
39 #include "wx/dialog.h"
40 #include "wx/msgdlg.h"
42 #include "wx/wxchar.h"
47 #include "wx/apptrait.h"
48 #include "wx/filename.h"
49 #include "wx/module.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/wrapcctl.h"
84 #include "wx/msw/wince/missing.h"
87 // instead of including <shlwapi.h> which is not part of the core SDK and not
88 // shipped at all with other compilers, we always define the parts of it we
89 // need here ourselves
91 // NB: DLLVER_PLATFORM_WINDOWS will be defined if shlwapi.h had been somehow
93 #ifndef DLLVER_PLATFORM_WINDOWS
94 // hopefully we don't need to change packing as DWORDs should be already
99 DWORD dwMajorVersion
; // Major version
100 DWORD dwMinorVersion
; // Minor version
101 DWORD dwBuildNumber
; // Build number
102 DWORD dwPlatformID
; // DLLVER_PLATFORM_*
105 typedef HRESULT (CALLBACK
* DLLGETVERSIONPROC
)(DLLVERSIONINFO
*);
106 #endif // defined(DLLVERSIONINFO)
109 // ---------------------------------------------------------------------------
111 // ---------------------------------------------------------------------------
113 extern wxList WXDLLEXPORT wxPendingDelete
;
115 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
116 extern void wxSetKeyboardHook(bool doIt
);
119 // NB: all "NoRedraw" classes must have the same names as the "normal" classes
120 // with NR suffix - wxWindow::MSWCreate() supposes this
122 WXDLLIMPEXP_CORE wxChar
*wxCanvasClassName
;
123 WXDLLIMPEXP_CORE wxChar
*wxCanvasClassNameNR
;
125 WXDLLIMPEXP_CORE
const wxChar
*wxCanvasClassName
= wxT("wxWindowClass");
126 WXDLLIMPEXP_CORE
const wxChar
*wxCanvasClassNameNR
= wxT("wxWindowClassNR");
128 WXDLLIMPEXP_CORE
const wxChar
*wxMDIFrameClassName
= wxT("wxMDIFrameClass");
129 WXDLLIMPEXP_CORE
const wxChar
*wxMDIFrameClassNameNoRedraw
= wxT("wxMDIFrameClassNR");
130 WXDLLIMPEXP_CORE
const wxChar
*wxMDIChildFrameClassName
= wxT("wxMDIChildFrameClass");
131 WXDLLIMPEXP_CORE
const wxChar
*wxMDIChildFrameClassNameNoRedraw
= wxT("wxMDIChildFrameClassNR");
133 // ----------------------------------------------------------------------------
135 // ----------------------------------------------------------------------------
137 LRESULT WXDLLEXPORT APIENTRY
wxWndProc(HWND
, UINT
, WPARAM
, LPARAM
);
139 // ===========================================================================
140 // wxGUIAppTraits implementation
141 // ===========================================================================
143 // private class which we use to pass parameters from BeforeChildWaitLoop() to
144 // AfterChildWaitLoop()
145 struct ChildWaitLoopData
147 ChildWaitLoopData(wxWindowDisabler
*wd_
, wxWindow
*winActive_
)
150 winActive
= winActive_
;
153 wxWindowDisabler
*wd
;
157 void *wxGUIAppTraits::BeforeChildWaitLoop()
160 We use a dirty hack here to disable all application windows (which we
161 must do because otherwise the calls to wxYield() could lead to some very
162 unexpected reentrancies in the users code) but to avoid losing
163 focus/activation entirely when the child process terminates which would
164 happen if we simply disabled everything using wxWindowDisabler. Indeed,
165 remember that Windows will never activate a disabled window and when the
166 last childs window is closed and Windows looks for a window to activate
167 all our windows are still disabled. There is no way to enable them in
168 time because we don't know when the childs windows are going to be
169 closed, so the solution we use here is to keep one special tiny frame
170 enabled all the time. Then when the child terminates it will get
171 activated and when we close it below -- after reenabling all the other
172 windows! -- the previously active window becomes activated again and
177 // first disable all existing windows
178 wxWindowDisabler
*wd
= new wxWindowDisabler
;
180 // then create an "invisible" frame: it has minimal size, is positioned
181 // (hopefully) outside the screen and doesn't appear on the taskbar
182 wxWindow
*winActive
= new wxFrame
184 wxTheApp
->GetTopWindow(),
187 wxPoint(32600, 32600),
189 wxDEFAULT_FRAME_STYLE
| wxFRAME_NO_TASKBAR
193 return new ChildWaitLoopData(wd
, winActive
);
196 void wxGUIAppTraits::AlwaysYield()
201 void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig
)
205 ChildWaitLoopData
* const data
= (ChildWaitLoopData
*)dataOrig
;
209 // finally delete the dummy frame and, as wd has been already destroyed and
210 // the other windows reenabled, the activation is going to return to the
211 // window which had had it before
212 data
->winActive
->Destroy();
214 // also delete the temporary data object itself
218 bool wxGUIAppTraits::DoMessageFromThreadWait()
220 // we should return false only if the app should exit, i.e. only if
221 // Dispatch() determines that the main event loop should terminate
222 wxEventLoop
*evtLoop
= wxEventLoop::GetActive();
223 if ( !evtLoop
|| !evtLoop
->Pending() )
225 // no events means no quit event
229 return evtLoop
->Dispatch();
232 wxToolkitInfo
& wxGUIAppTraits::GetToolkitInfo()
234 static wxToolkitInfo info
;
235 wxToolkitInfo
& baseInfo
= wxAppTraits::GetToolkitInfo();
236 info
.versionMajor
= baseInfo
.versionMajor
;
237 info
.versionMinor
= baseInfo
.versionMinor
;
238 info
.os
= baseInfo
.os
;
239 info
.shortName
= _T("msw");
240 info
.name
= _T("wxMSW");
241 #ifdef __WXUNIVERSAL__
242 info
.shortName
<< _T("univ");
243 info
.name
<< _T("/wxUniversal");
248 // ===========================================================================
249 // wxApp implementation
250 // ===========================================================================
252 int wxApp::m_nCmdShow
= SW_SHOWNORMAL
;
254 // ---------------------------------------------------------------------------
256 // ---------------------------------------------------------------------------
258 IMPLEMENT_DYNAMIC_CLASS(wxApp
, wxEvtHandler
)
260 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
261 EVT_IDLE(wxApp::OnIdle
)
262 EVT_END_SESSION(wxApp::OnEndSession
)
263 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession
)
266 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
268 class wxCallBaseCleanup
271 wxCallBaseCleanup(wxApp
*app
) : m_app(app
) { }
272 ~wxCallBaseCleanup() { if ( m_app
) m_app
->wxAppBase::CleanUp(); }
274 void Dismiss() { m_app
= NULL
; }
281 bool wxApp::Initialize(int& argc
, wxChar
**argv
)
283 if ( !wxAppBase::Initialize(argc
, argv
) )
286 // ensure that base cleanup is done if we return too early
287 wxCallBaseCleanup
callBaseCleanup(this);
290 wxString tmp
= GetAppName();
291 tmp
+= wxT("ClassName");
292 wxCanvasClassName
= wxStrdup( tmp
.c_str() );
294 wxCanvasClassNameNR
= wxStrdup( tmp
.c_str() );
295 HWND hWnd
= FindWindow( wxCanvasClassNameNR
, NULL
);
298 SetForegroundWindow( (HWND
)(((DWORD
)hWnd
)|0x01) );
303 #if !defined(__WXMICROWIN__)
304 InitCommonControls();
305 #endif // !defined(__WXMICROWIN__)
307 #if defined(__SMARTPHONE__) || defined(__POCKETPC__)
308 SHInitExtraControls();
312 // Don't show a message box if a function such as SHGetFileInfo
313 // fails to find a device.
314 SetErrorMode(SEM_FAILCRITICALERRORS
|SEM_NOOPENFILEERRORBOX
);
319 RegisterWindowClasses();
321 wxWinHandleHash
= new wxWinHashTable(wxKEY_INTEGER
, 100);
323 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
324 wxSetKeyboardHook(true);
327 callBaseCleanup
.Dismiss();
332 // ---------------------------------------------------------------------------
333 // RegisterWindowClasses
334 // ---------------------------------------------------------------------------
336 // TODO we should only register classes really used by the app. For this it
337 // would be enough to just delay the class registration until an attempt
338 // to create a window of this class is made.
339 bool wxApp::RegisterWindowClasses()
342 wxZeroMemory(wndclass
);
344 // for each class we register one with CS_(V|H)REDRAW style and one
345 // without for windows created with wxNO_FULL_REDRAW_ON_REPAINT flag
346 static const long styleNormal
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
;
347 static const long styleNoRedraw
= CS_DBLCLKS
;
349 // the fields which are common to all classes
350 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
351 wndclass
.hInstance
= wxhInstance
;
352 wndclass
.hCursor
= ::LoadCursor((HINSTANCE
)NULL
, IDC_ARROW
);
354 // register the class for all normal windows
355 wndclass
.hbrBackground
= (HBRUSH
)(COLOR_BTNFACE
+ 1);
356 wndclass
.lpszClassName
= wxCanvasClassName
;
357 wndclass
.style
= styleNormal
;
359 if ( !RegisterClass(&wndclass
) )
361 wxLogLastError(wxT("RegisterClass(frame)"));
365 wndclass
.lpszClassName
= wxCanvasClassNameNR
;
366 wndclass
.style
= styleNoRedraw
;
368 if ( !RegisterClass(&wndclass
) )
370 wxLogLastError(wxT("RegisterClass(no redraw frame)"));
373 // Register the MDI frame window class.
374 wndclass
.hbrBackground
= (HBRUSH
)NULL
; // paint MDI frame ourselves
375 wndclass
.lpszClassName
= wxMDIFrameClassName
;
376 wndclass
.style
= styleNormal
;
378 if ( !RegisterClass(&wndclass
) )
380 wxLogLastError(wxT("RegisterClass(MDI parent)"));
383 // "no redraw" MDI frame
384 wndclass
.lpszClassName
= wxMDIFrameClassNameNoRedraw
;
385 wndclass
.style
= styleNoRedraw
;
387 if ( !RegisterClass(&wndclass
) )
389 wxLogLastError(wxT("RegisterClass(no redraw MDI parent frame)"));
392 // Register the MDI child frame window class.
393 wndclass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
394 wndclass
.lpszClassName
= wxMDIChildFrameClassName
;
395 wndclass
.style
= styleNormal
;
397 if ( !RegisterClass(&wndclass
) )
399 wxLogLastError(wxT("RegisterClass(MDI child)"));
402 // "no redraw" MDI child frame
403 wndclass
.lpszClassName
= wxMDIChildFrameClassNameNoRedraw
;
404 wndclass
.style
= styleNoRedraw
;
406 if ( !RegisterClass(&wndclass
) )
408 wxLogLastError(wxT("RegisterClass(no redraw MDI child)"));
414 // ---------------------------------------------------------------------------
415 // UnregisterWindowClasses
416 // ---------------------------------------------------------------------------
418 bool wxApp::UnregisterWindowClasses()
422 #ifndef __WXMICROWIN__
423 // MDI frame window class.
424 if ( !::UnregisterClass(wxMDIFrameClassName
, wxhInstance
) )
426 wxLogLastError(wxT("UnregisterClass(MDI parent)"));
431 // "no redraw" MDI frame
432 if ( !::UnregisterClass(wxMDIFrameClassNameNoRedraw
, wxhInstance
) )
434 wxLogLastError(wxT("UnregisterClass(no redraw MDI parent frame)"));
439 // MDI child frame window class.
440 if ( !::UnregisterClass(wxMDIChildFrameClassName
, wxhInstance
) )
442 wxLogLastError(wxT("UnregisterClass(MDI child)"));
447 // "no redraw" MDI child frame
448 if ( !::UnregisterClass(wxMDIChildFrameClassNameNoRedraw
, wxhInstance
) )
450 wxLogLastError(wxT("UnregisterClass(no redraw MDI child)"));
456 if ( !::UnregisterClass(wxCanvasClassName
, wxhInstance
) )
458 wxLogLastError(wxT("UnregisterClass(canvas)"));
463 if ( !::UnregisterClass(wxCanvasClassNameNR
, wxhInstance
) )
465 wxLogLastError(wxT("UnregisterClass(no redraw canvas)"));
469 #endif // __WXMICROWIN__
474 void wxApp::CleanUp()
476 // all objects pending for deletion must be deleted first, otherwise we
477 // would crash when they use wxWinHandleHash (and UnregisterWindowClasses()
478 // call wouldn't succeed as long as any windows still exist), so call the
479 // base class method first and only then do our clean up
480 wxAppBase::CleanUp();
482 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
483 wxSetKeyboardHook(false);
488 // for an EXE the classes are unregistered when it terminates but DLL may
489 // be loaded several times (load/unload/load) into the same process in
490 // which case the registration will fail after the first time if we don't
491 // unregister the classes now
492 UnregisterWindowClasses();
494 delete wxWinHandleHash
;
495 wxWinHandleHash
= NULL
;
498 free( wxCanvasClassName
);
499 free( wxCanvasClassNameNR
);
503 // ----------------------------------------------------------------------------
505 // ----------------------------------------------------------------------------
509 m_printMode
= wxPRINT_WINDOWS
;
516 // ----------------------------------------------------------------------------
517 // wxApp idle handling
518 // ----------------------------------------------------------------------------
520 void wxApp::OnIdle(wxIdleEvent
& event
)
522 wxAppBase::OnIdle(event
);
524 #if wxUSE_DC_CACHEING
525 // automated DC cache management: clear the cached DCs and bitmap
526 // if it's likely that the app has finished with them, that is, we
527 // get an idle event and we're not dragging anything.
528 if (!::GetKeyState(MK_LBUTTON
) && !::GetKeyState(MK_MBUTTON
) && !::GetKeyState(MK_RBUTTON
))
530 #endif // wxUSE_DC_CACHEING
533 void wxApp::WakeUpIdle()
535 // Send the top window a dummy message so idle handler processing will
536 // start up again. Doing it this way ensures that the idle handler
537 // wakes up in the right thread (see also wxWakeUpMainThread() which does
538 // the same for the main app thread only)
539 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
542 if ( !::PostMessage(GetHwndOf(topWindow
), WM_NULL
, 0, 0) )
544 // should never happen
545 wxLogLastError(wxT("PostMessage(WM_NULL)"));
550 // ----------------------------------------------------------------------------
551 // other wxApp event hanlders
552 // ----------------------------------------------------------------------------
554 void wxApp::OnEndSession(wxCloseEvent
& WXUNUSED(event
))
557 GetTopWindow()->Close(true);
560 // Default behaviour: close the application with prompts. The
561 // user can veto the close, and therefore the end session.
562 void wxApp::OnQueryEndSession(wxCloseEvent
& event
)
566 if (!GetTopWindow()->Close(!event
.CanVeto()))
571 // ----------------------------------------------------------------------------
573 // ----------------------------------------------------------------------------
576 int wxApp::GetComCtl32Version()
578 #if defined(__WXMICROWIN__) || defined(__WXWINCE__)
583 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
584 // but as its value should be the same both times it doesn't matter
585 static int s_verComCtl32
= -1;
587 if ( s_verComCtl32
== -1 )
589 // initally assume no comctl32.dll at all
592 // we're prepared to handle the errors
595 #if wxUSE_DYNLIB_CLASS
597 wxDynamicLibrary
dllComCtl32(_T("comctl32.dll"), wxDL_VERBATIM
);
599 // if so, then we can check for the version
600 if ( dllComCtl32
.IsLoaded() )
602 // now check if the function is available during run-time
603 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC
, DllGetVersion
, dllComCtl32
);
604 if ( pfnDllGetVersion
)
607 dvi
.cbSize
= sizeof(dvi
);
609 HRESULT hr
= (*pfnDllGetVersion
)(&dvi
);
612 wxLogApiError(_T("DllGetVersion"), hr
);
616 // this is incompatible with _WIN32_IE values, but
617 // compatible with the other values returned by
618 // GetComCtl32Version()
619 s_verComCtl32
= 100*dvi
.dwMajorVersion
+
624 // if DllGetVersion() is unavailable either during compile or
625 // run-time, try to guess the version otherwise
626 if ( !s_verComCtl32
)
628 // InitCommonControlsEx is unique to 4.70 and later
629 void *pfn
= dllComCtl32
.GetSymbol(_T("InitCommonControlsEx"));
632 // not found, must be 4.00
637 // many symbols appeared in comctl32 4.71, could use any of
638 // them except may be DllInstall()
639 pfn
= dllComCtl32
.GetSymbol(_T("InitializeFlatSB"));
642 // not found, must be 4.70
647 // found, must be 4.71 or later
656 return s_verComCtl32
;
657 #endif // Microwin/!Microwin
660 // Yield to incoming messages
662 bool wxApp::Yield(bool onlyIfNeeded
)
665 static bool s_inYield
= false;
668 // disable log flushing from here because a call to wxYield() shouldn't
669 // normally result in message boxes popping up &c
677 wxFAIL_MSG( wxT("wxYield called recursively" ) );
685 // we don't want to process WM_QUIT from here - it should be processed in
686 // the main event loop in order to stop it
688 while ( PeekMessage(&msg
, (HWND
)0, 0, 0, PM_NOREMOVE
) &&
689 msg
.message
!= WM_QUIT
)
692 wxMutexGuiLeaveOrEnter();
693 #endif // wxUSE_THREADS
695 if ( !wxTheApp
->Dispatch() )
699 // if there are pending events, we must process them.
700 ProcessPendingEvents();
703 // let the logs be flashed again
714 // ----------------------------------------------------------------------------
715 // exception handling
716 // ----------------------------------------------------------------------------
718 bool wxApp::OnExceptionInMainLoop()
720 // ask the user about what to do: use the Win32 API function here as it
721 // could be dangerous to use any wxWidgets code in this state
726 _T("An unhandled exception occurred. Press \"Abort\" to \
727 terminate the program,\r\n\
728 \"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
729 _T("Unhandled exception"),
730 MB_ABORTRETRYIGNORE
|
740 wxFAIL_MSG( _T("unexpected MessageBox() return code") );
751 #endif // wxUSE_EXCEPTIONS
753 // ----------------------------------------------------------------------------
754 // deprecated event loop functions
755 // ----------------------------------------------------------------------------
757 #if WXWIN_COMPATIBILITY_2_4
759 void wxApp::DoMessage(WXMSG
*pMsg
)
761 wxEventLoop
*evtLoop
= wxEventLoop::GetActive();
763 evtLoop
->ProcessMessage(pMsg
);
766 bool wxApp::DoMessage()
768 wxEventLoop
*evtLoop
= wxEventLoop::GetActive();
769 return evtLoop
? evtLoop
->Dispatch() : false;
772 bool wxApp::ProcessMessage(WXMSG
* pMsg
)
774 wxEventLoop
*evtLoop
= wxEventLoop::GetActive();
775 return evtLoop
&& evtLoop
->PreProcessMessage(pMsg
);
778 #endif // WXWIN_COMPATIBILITY_2_4