1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "app.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
27 #if defined(__BORLANDC__)
35 #include "wx/gdicmn.h"
38 #include "wx/cursor.h"
40 #include "wx/palette.h"
42 #include "wx/dialog.h"
43 #include "wx/msgdlg.h"
45 #include "wx/dynarray.h"
46 #include "wx/wxchar.h"
51 #include "wx/apptrait.h"
52 #include "wx/filename.h"
53 #include "wx/module.h"
54 #include "wx/dynlib.h"
56 #include "wx/msw/private.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
77 #include "wx/msw/wrapcctl.h"
81 #include "wx/msw/wince/missing.h"
84 #if (!defined(__MINGW32__) || wxCHECK_W32API_VERSION( 2, 0 )) && \
85 !defined(__CYGWIN__) && !defined(__DIGITALMARS__) && !defined(__WXWINCE__) && \
86 (!defined(_MSC_VER) || (_MSC_VER > 1100))
90 // ---------------------------------------------------------------------------
92 // ---------------------------------------------------------------------------
94 extern wxList WXDLLEXPORT wxPendingDelete
;
96 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
97 extern void wxSetKeyboardHook(bool doIt
);
100 // NB: all "NoRedraw" classes must have the same names as the "normal" classes
101 // with NR suffix - wxWindow::MSWCreate() supposes this
103 wxChar
*wxCanvasClassName
;
104 wxChar
*wxCanvasClassNameNR
;
106 const wxChar
*wxCanvasClassName
= wxT("wxWindowClass");
107 const wxChar
*wxCanvasClassNameNR
= wxT("wxWindowClassNR");
109 const wxChar
*wxMDIFrameClassName
= wxT("wxMDIFrameClass");
110 const wxChar
*wxMDIFrameClassNameNoRedraw
= wxT("wxMDIFrameClassNR");
111 const wxChar
*wxMDIChildFrameClassName
= wxT("wxMDIChildFrameClass");
112 const wxChar
*wxMDIChildFrameClassNameNoRedraw
= wxT("wxMDIChildFrameClassNR");
114 HBRUSH wxDisableButtonBrush
= (HBRUSH
) 0;
116 // ----------------------------------------------------------------------------
118 // ----------------------------------------------------------------------------
120 LRESULT WXDLLEXPORT APIENTRY
wxWndProc(HWND
, UINT
, WPARAM
, LPARAM
);
122 // ===========================================================================
123 // wxGUIAppTraits implementation
124 // ===========================================================================
126 // private class which we use to pass parameters from BeforeChildWaitLoop() to
127 // AfterChildWaitLoop()
128 struct ChildWaitLoopData
130 ChildWaitLoopData(wxWindowDisabler
*wd_
, wxWindow
*winActive_
)
133 winActive
= winActive_
;
136 wxWindowDisabler
*wd
;
140 void *wxGUIAppTraits::BeforeChildWaitLoop()
143 We use a dirty hack here to disable all application windows (which we
144 must do because otherwise the calls to wxYield() could lead to some very
145 unexpected reentrancies in the users code) but to avoid losing
146 focus/activation entirely when the child process terminates which would
147 happen if we simply disabled everything using wxWindowDisabler. Indeed,
148 remember that Windows will never activate a disabled window and when the
149 last childs window is closed and Windows looks for a window to activate
150 all our windows are still disabled. There is no way to enable them in
151 time because we don't know when the childs windows are going to be
152 closed, so the solution we use here is to keep one special tiny frame
153 enabled all the time. Then when the child terminates it will get
154 activated and when we close it below -- after reenabling all the other
155 windows! -- the previously active window becomes activated again and
160 // first disable all existing windows
161 wxWindowDisabler
*wd
= new wxWindowDisabler
;
163 // then create an "invisible" frame: it has minimal size, is positioned
164 // (hopefully) outside the screen and doesn't appear on the taskbar
165 wxWindow
*winActive
= new wxFrame
167 wxTheApp
->GetTopWindow(),
170 wxPoint(32600, 32600),
172 wxDEFAULT_FRAME_STYLE
| wxFRAME_NO_TASKBAR
176 return new ChildWaitLoopData(wd
, winActive
);
179 void wxGUIAppTraits::AlwaysYield()
184 void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig
)
188 const ChildWaitLoopData
* const data
= (ChildWaitLoopData
*)dataOrig
;
192 // finally delete the dummy frame and, as wd has been already destroyed and
193 // the other windows reenabled, the activation is going to return to the
194 // window which had had it before
195 data
->winActive
->Destroy();
198 bool wxGUIAppTraits::DoMessageFromThreadWait()
200 // we should return false only if the app should exit, i.e. only if
201 // Dispatch() determines that the main event loop should terminate
202 return !wxTheApp
|| wxTheApp
->Dispatch();
205 wxToolkitInfo
& wxGUIAppTraits::GetToolkitInfo()
207 static wxToolkitInfo info
;
208 wxToolkitInfo
& baseInfo
= wxAppTraits::GetToolkitInfo();
209 info
.versionMajor
= baseInfo
.versionMajor
;
210 info
.versionMinor
= baseInfo
.versionMinor
;
211 info
.os
= baseInfo
.os
;
212 info
.shortName
= _T("msw");
213 info
.name
= _T("wxMSW");
214 #ifdef __WXUNIVERSAL__
215 info
.shortName
<< _T("univ");
216 info
.name
<< _T("/wxUniversal");
221 // ===========================================================================
222 // wxApp implementation
223 // ===========================================================================
225 int wxApp::m_nCmdShow
= SW_SHOWNORMAL
;
227 // ---------------------------------------------------------------------------
229 // ---------------------------------------------------------------------------
231 IMPLEMENT_DYNAMIC_CLASS(wxApp
, wxEvtHandler
)
233 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
234 EVT_IDLE(wxApp::OnIdle
)
235 EVT_END_SESSION(wxApp::OnEndSession
)
236 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession
)
239 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
241 class wxCallBaseCleanup
244 wxCallBaseCleanup(wxApp
*app
) : m_app(app
) { }
245 ~wxCallBaseCleanup() { if ( m_app
) m_app
->wxAppBase::CleanUp(); }
247 void Dismiss() { m_app
= NULL
; }
254 bool wxApp::Initialize(int& argc
, wxChar
**argv
)
256 if ( !wxAppBase::Initialize(argc
, argv
) )
259 // ensure that base cleanup is done if we return too early
260 wxCallBaseCleanup
callBaseCleanup(this);
263 wxString tmp
= GetAppName();
264 tmp
+= wxT("ClassName");
265 wxCanvasClassName
= wxStrdup( tmp
.c_str() );
267 wxCanvasClassNameNR
= wxStrdup( tmp
.c_str() );
268 HWND hWnd
= FindWindow( wxCanvasClassNameNR
, NULL
);
271 SetForegroundWindow( (HWND
)(((DWORD
)hWnd
)|0x01) );
276 // the first thing to do is to check if we're trying to run an Unicode
277 // program under Win9x w/o MSLU emulation layer - if so, abort right now
278 // as it has no chance to work
279 #if wxUSE_UNICODE && !wxUSE_UNICODE_MSLU
280 if ( wxGetOsVersion() != wxWINDOWS_NT
&& wxGetOsVersion() != wxWINDOWS_CE
)
282 // note that we can use MessageBoxW() as it's implemented even under
283 // Win9x - OTOH, we can't use wxGetTranslation() because the file APIs
284 // used by wxLocale are not
288 _T("This program uses Unicode and requires Windows NT/2000/XP/CE.\nProgram aborted."),
289 _T("wxWidgets Fatal Error"),
295 #endif // wxUSE_UNICODE && !wxUSE_UNICODE_MSLU
297 #if defined(__WIN95__) && !defined(__WXMICROWIN__)
298 InitCommonControls();
301 #if wxUSE_OLE || wxUSE_DRAG_AND_DROP
304 // we need to initialize OLE library
306 if ( FAILED(::CoInitializeEx(NULL
, COINIT_MULTITHREADED
)) )
307 wxLogError(_("Cannot initialize OLE"));
309 if ( FAILED(::OleInitialize(NULL
)) )
310 wxLogError(_("Cannot initialize OLE"));
317 if (!Ctl3dRegister(wxhInstance
))
318 wxLogError(wxT("Cannot register CTL3D"));
320 Ctl3dAutoSubclass(wxhInstance
);
321 #endif // wxUSE_CTL3D
323 RegisterWindowClasses();
325 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
326 // Create the brush for disabling bitmap buttons
328 lb
.lbStyle
= BS_PATTERN
;
330 lb
.lbHatch
= (int)LoadBitmap( wxhInstance
, wxT("wxDISABLE_BUTTON_BITMAP") );
333 wxDisableButtonBrush
= ::CreateBrushIndirect( &lb
);
334 ::DeleteObject( (HGDIOBJ
)lb
.lbHatch
);
336 //else: wxWidgets resources are probably not linked in
337 #endif // !__WXMICROWIN__ && !__WXWINCE__
343 wxWinHandleHash
= new wxWinHashTable(wxKEY_INTEGER
, 100);
345 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
346 wxSetKeyboardHook(true);
349 callBaseCleanup
.Dismiss();
354 // ---------------------------------------------------------------------------
355 // RegisterWindowClasses
356 // ---------------------------------------------------------------------------
358 // TODO we should only register classes really used by the app. For this it
359 // would be enough to just delay the class registration until an attempt
360 // to create a window of this class is made.
361 bool wxApp::RegisterWindowClasses()
364 wxZeroMemory(wndclass
);
366 // for each class we register one with CS_(V|H)REDRAW style and one
367 // without for windows created with wxNO_FULL_REDRAW_ON_REPAINT flag
368 static const long styleNormal
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
;
369 static const long styleNoRedraw
= CS_DBLCLKS
;
371 // the fields which are common to all classes
372 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
373 wndclass
.hInstance
= wxhInstance
;
374 wndclass
.hCursor
= ::LoadCursor((HINSTANCE
)NULL
, IDC_ARROW
);
376 // Register the frame window class.
377 wndclass
.hbrBackground
= (HBRUSH
)(COLOR_APPWORKSPACE
+ 1);
378 wndclass
.lpszClassName
= wxCanvasClassName
;
379 wndclass
.style
= styleNormal
;
381 if ( !RegisterClass(&wndclass
) )
383 wxLogLastError(wxT("RegisterClass(frame)"));
387 wndclass
.lpszClassName
= wxCanvasClassNameNR
;
388 wndclass
.style
= styleNoRedraw
;
390 if ( !RegisterClass(&wndclass
) )
392 wxLogLastError(wxT("RegisterClass(no redraw frame)"));
395 // Register the MDI frame window class.
396 wndclass
.hbrBackground
= (HBRUSH
)NULL
; // paint MDI frame ourselves
397 wndclass
.lpszClassName
= wxMDIFrameClassName
;
398 wndclass
.style
= styleNormal
;
400 if ( !RegisterClass(&wndclass
) )
402 wxLogLastError(wxT("RegisterClass(MDI parent)"));
405 // "no redraw" MDI frame
406 wndclass
.lpszClassName
= wxMDIFrameClassNameNoRedraw
;
407 wndclass
.style
= styleNoRedraw
;
409 if ( !RegisterClass(&wndclass
) )
411 wxLogLastError(wxT("RegisterClass(no redraw MDI parent frame)"));
414 // Register the MDI child frame window class.
415 wndclass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
416 wndclass
.lpszClassName
= wxMDIChildFrameClassName
;
417 wndclass
.style
= styleNormal
;
419 if ( !RegisterClass(&wndclass
) )
421 wxLogLastError(wxT("RegisterClass(MDI child)"));
424 // "no redraw" MDI child frame
425 wndclass
.lpszClassName
= wxMDIChildFrameClassNameNoRedraw
;
426 wndclass
.style
= styleNoRedraw
;
428 if ( !RegisterClass(&wndclass
) )
430 wxLogLastError(wxT("RegisterClass(no redraw MDI child)"));
436 // ---------------------------------------------------------------------------
437 // UnregisterWindowClasses
438 // ---------------------------------------------------------------------------
440 bool wxApp::UnregisterWindowClasses()
444 #ifndef __WXMICROWIN__
445 // MDI frame window class.
446 if ( !::UnregisterClass(wxMDIFrameClassName
, wxhInstance
) )
448 wxLogLastError(wxT("UnregisterClass(MDI parent)"));
453 // "no redraw" MDI frame
454 if ( !::UnregisterClass(wxMDIFrameClassNameNoRedraw
, wxhInstance
) )
456 wxLogLastError(wxT("UnregisterClass(no redraw MDI parent frame)"));
461 // MDI child frame window class.
462 if ( !::UnregisterClass(wxMDIChildFrameClassName
, wxhInstance
) )
464 wxLogLastError(wxT("UnregisterClass(MDI child)"));
469 // "no redraw" MDI child frame
470 if ( !::UnregisterClass(wxMDIChildFrameClassNameNoRedraw
, wxhInstance
) )
472 wxLogLastError(wxT("UnregisterClass(no redraw MDI child)"));
478 if ( !::UnregisterClass(wxCanvasClassName
, wxhInstance
) )
480 wxLogLastError(wxT("UnregisterClass(canvas)"));
485 if ( !::UnregisterClass(wxCanvasClassNameNR
, wxhInstance
) )
487 wxLogLastError(wxT("UnregisterClass(no redraw canvas)"));
491 #endif // __WXMICROWIN__
496 void wxApp::CleanUp()
498 // all objects pending for deletion must be deleted first, otherwise we
499 // would crash when they use wxWinHandleHash (and UnregisterWindowClasses()
500 // call wouldn't succeed as long as any windows still exist), so call the
501 // base class method first and only then do our clean up
502 wxAppBase::CleanUp();
504 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
505 wxSetKeyboardHook(false);
512 if ( wxDisableButtonBrush
)
513 ::DeleteObject( wxDisableButtonBrush
);
523 // for an EXE the classes are unregistered when it terminates but DLL may
524 // be loaded several times (load/unload/load) into the same process in
525 // which case the registration will fail after the first time if we don't
526 // unregister the classes now
527 UnregisterWindowClasses();
530 Ctl3dUnregister(wxhInstance
);
533 delete wxWinHandleHash
;
534 wxWinHandleHash
= NULL
;
537 free( wxCanvasClassName
);
538 free( wxCanvasClassNameNR
);
542 // ----------------------------------------------------------------------------
544 // ----------------------------------------------------------------------------
548 m_printMode
= wxPRINT_WINDOWS
;
553 // our cmd line arguments are allocated inside wxEntry(HINSTANCE), they
554 // don't come from main(), so we have to free them
558 // m_argv elements were allocated by wxStrdup()
562 // but m_argv itself -- using new[]
566 // ----------------------------------------------------------------------------
567 // wxApp idle handling
568 // ----------------------------------------------------------------------------
570 void wxApp::OnIdle(wxIdleEvent
& event
)
572 wxAppBase::OnIdle(event
);
574 #if wxUSE_DC_CACHEING
575 // automated DC cache management: clear the cached DCs and bitmap
576 // if it's likely that the app has finished with them, that is, we
577 // get an idle event and we're not dragging anything.
578 if (!::GetKeyState(MK_LBUTTON
) && !::GetKeyState(MK_MBUTTON
) && !::GetKeyState(MK_RBUTTON
))
580 #endif // wxUSE_DC_CACHEING
583 void wxApp::WakeUpIdle()
585 // Send the top window a dummy message so idle handler processing will
586 // start up again. Doing it this way ensures that the idle handler
587 // wakes up in the right thread (see also wxWakeUpMainThread() which does
588 // the same for the main app thread only)
589 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
592 if ( !::PostMessage(GetHwndOf(topWindow
), WM_NULL
, 0, 0) )
594 // should never happen
595 wxLogLastError(wxT("PostMessage(WM_NULL)"));
600 // ----------------------------------------------------------------------------
601 // other wxApp event hanlders
602 // ----------------------------------------------------------------------------
604 void wxApp::OnEndSession(wxCloseEvent
& WXUNUSED(event
))
607 GetTopWindow()->Close(true);
610 // Default behaviour: close the application with prompts. The
611 // user can veto the close, and therefore the end session.
612 void wxApp::OnQueryEndSession(wxCloseEvent
& event
)
616 if (!GetTopWindow()->Close(!event
.CanVeto()))
621 // ----------------------------------------------------------------------------
623 // ----------------------------------------------------------------------------
626 int wxApp::GetComCtl32Version()
628 #if defined(__WXMICROWIN__) || defined(__WXWINCE__)
633 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
634 // but as its value should be the same both times it doesn't matter
635 static int s_verComCtl32
= -1;
637 if ( s_verComCtl32
== -1 )
639 // initally assume no comctl32.dll at all
642 // we're prepared to handle the errors
646 wxDynamicLibrary
dllComCtl32(_T("comctl32.dll"), wxDL_VERBATIM
);
648 // if so, then we can check for the version
649 if ( dllComCtl32
.IsLoaded() )
651 #ifdef DLLVER_PLATFORM_WINDOWS
652 // try to use DllGetVersion() if available in _headers_
653 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC
, DllGetVersion
, dllComCtl32
);
654 if ( pfnDllGetVersion
)
657 dvi
.cbSize
= sizeof(dvi
);
659 HRESULT hr
= (*pfnDllGetVersion
)(&dvi
);
662 wxLogApiError(_T("DllGetVersion"), hr
);
666 // this is incompatible with _WIN32_IE values, but
667 // compatible with the other values returned by
668 // GetComCtl32Version()
669 s_verComCtl32
= 100*dvi
.dwMajorVersion
+
675 // if DllGetVersion() is unavailable either during compile or
676 // run-time, try to guess the version otherwise
677 if ( !s_verComCtl32
)
679 // InitCommonControlsEx is unique to 4.70 and later
680 void *pfn
= dllComCtl32
.GetSymbol(_T("InitCommonControlsEx"));
683 // not found, must be 4.00
688 // many symbols appeared in comctl32 4.71, could use any of
689 // them except may be DllInstall()
690 pfn
= dllComCtl32
.GetSymbol(_T("InitializeFlatSB"));
693 // not found, must be 4.70
698 // found, must be 4.71 or later
706 return s_verComCtl32
;
707 #endif // Microwin/!Microwin
710 // Yield to incoming messages
712 bool wxApp::Yield(bool onlyIfNeeded
)
715 static bool s_inYield
= false;
718 // disable log flushing from here because a call to wxYield() shouldn't
719 // normally result in message boxes popping up &c
727 wxFAIL_MSG( wxT("wxYield called recursively" ) );
735 // we don't want to process WM_QUIT from here - it should be processed in
736 // the main event loop in order to stop it
738 while ( PeekMessage(&msg
, (HWND
)0, 0, 0, PM_NOREMOVE
) &&
739 msg
.message
!= WM_QUIT
)
742 wxMutexGuiLeaveOrEnter();
743 #endif // wxUSE_THREADS
745 if ( !wxTheApp
->Dispatch() )
749 // if there are pending events, we must process them.
750 ProcessPendingEvents();
753 // let the logs be flashed again
764 // ----------------------------------------------------------------------------
765 // exception handling
766 // ----------------------------------------------------------------------------
768 bool wxApp::OnExceptionInMainLoop()
770 // ask the user about what to do: use the Win32 API function here as it
771 // could be dangerous to use any wxWidgets code in this state
776 _T("An unhandled exception occurred. Press \"Abort\" to \
777 terminate the program,\r\n\
778 \"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
779 _T("Unhandled exception"),
780 MB_ABORTRETRYIGNORE
|
790 wxFAIL_MSG( _T("unexpected MessageBox() return code") );
801 #endif // wxUSE_EXCEPTIONS