]> git.saurik.com Git - wxWidgets.git/blob - src/msw/app.cpp
Don't document wxSortedArrayString as deriving from wxArrayString.
[wxWidgets.git] / src / msw / app.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/app.cpp
3 // Purpose: wxApp
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ===========================================================================
12 // declarations
13 // ===========================================================================
14
15 // ---------------------------------------------------------------------------
16 // headers
17 // ---------------------------------------------------------------------------
18
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #if defined(__BORLANDC__)
23 #pragma hdrstop
24 #endif
25
26 #ifndef WX_PRECOMP
27 #include "wx/msw/wrapcctl.h"
28 #include "wx/dynarray.h"
29 #include "wx/frame.h"
30 #include "wx/app.h"
31 #include "wx/utils.h"
32 #include "wx/gdicmn.h"
33 #include "wx/pen.h"
34 #include "wx/brush.h"
35 #include "wx/cursor.h"
36 #include "wx/icon.h"
37 #include "wx/palette.h"
38 #include "wx/dc.h"
39 #include "wx/dialog.h"
40 #include "wx/msgdlg.h"
41 #include "wx/intl.h"
42 #include "wx/crt.h"
43 #include "wx/log.h"
44 #include "wx/module.h"
45 #endif
46
47 #include "wx/apptrait.h"
48 #include "wx/filename.h"
49 #include "wx/dynlib.h"
50 #include "wx/evtloop.h"
51 #include "wx/thread.h"
52 #include "wx/scopeguard.h"
53 #include "wx/vector.h"
54
55 #include "wx/msw/private.h"
56 #include "wx/msw/dc.h"
57 #include "wx/msw/ole/oleutils.h"
58 #include "wx/msw/private/timer.h"
59
60 #if wxUSE_TOOLTIPS
61 #include "wx/tooltip.h"
62 #endif // wxUSE_TOOLTIPS
63
64 // OLE is used for drag-and-drop, clipboard, OLE Automation..., but some
65 // compilers don't support it (missing headers, libs, ...)
66 #if defined(__GNUWIN32_OLD__) || defined(__SYMANTEC__)
67 #undef wxUSE_OLE
68
69 #define wxUSE_OLE 0
70 #endif // broken compilers
71
72 #if defined(__POCKETPC__) || defined(__SMARTPHONE__)
73 #include <ole2.h>
74 #include <aygshell.h>
75 #endif
76
77 #if wxUSE_OLE
78 #include <ole2.h>
79 #endif
80
81 #include <string.h>
82 #include <ctype.h>
83
84 #include "wx/msw/missing.h"
85
86 // instead of including <shlwapi.h> which is not part of the core SDK and not
87 // shipped at all with other compilers, we always define the parts of it we
88 // need here ourselves
89 //
90 // NB: DLLVER_PLATFORM_WINDOWS will be defined if shlwapi.h had been somehow
91 // included already
92 #ifndef DLLVER_PLATFORM_WINDOWS
93 // hopefully we don't need to change packing as DWORDs should be already
94 // correctly aligned
95 struct DLLVERSIONINFO
96 {
97 DWORD cbSize;
98 DWORD dwMajorVersion; // Major version
99 DWORD dwMinorVersion; // Minor version
100 DWORD dwBuildNumber; // Build number
101 DWORD dwPlatformID; // DLLVER_PLATFORM_*
102 };
103
104 typedef HRESULT (CALLBACK* DLLGETVERSIONPROC)(DLLVERSIONINFO *);
105 #endif // defined(DLLVERSIONINFO)
106
107 #ifndef ATTACH_PARENT_PROCESS
108 #define ATTACH_PARENT_PROCESS ((DWORD)-1)
109 #endif
110
111 // ---------------------------------------------------------------------------
112 // global variables
113 // ---------------------------------------------------------------------------
114
115 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
116 extern void wxSetKeyboardHook(bool doIt);
117 #endif
118
119 // because of mingw32 4.3 bug this struct can't be inside the namespace below:
120 // see http://article.gmane.org/gmane.comp.lib.wxwidgets.devel/110282
121 struct ClassRegInfo
122 {
123 ClassRegInfo(const wxChar *name)
124 : regname(name),
125 regnameNR(regname + wxApp::GetNoRedrawClassSuffix())
126 {
127 }
128
129 // the name of the registered class with and without CS_[HV]REDRAW styles
130 wxString regname;
131 wxString regnameNR;
132 };
133
134 namespace
135 {
136
137 wxVector<ClassRegInfo> gs_regClassesInfo;
138
139 } // anonymous namespace
140
141 // ----------------------------------------------------------------------------
142 // private functions
143 // ----------------------------------------------------------------------------
144
145 LRESULT WXDLLEXPORT APIENTRY wxWndProc(HWND, UINT, WPARAM, LPARAM);
146
147 // ===========================================================================
148 // wxGUIAppTraits implementation
149 // ===========================================================================
150
151 // private class which we use to pass parameters from BeforeChildWaitLoop() to
152 // AfterChildWaitLoop()
153 struct ChildWaitLoopData
154 {
155 ChildWaitLoopData(wxWindowDisabler *wd_, wxWindow *winActive_)
156 {
157 wd = wd_;
158 winActive = winActive_;
159 }
160
161 wxWindowDisabler *wd;
162 wxWindow *winActive;
163 };
164
165 void *wxGUIAppTraits::BeforeChildWaitLoop()
166 {
167 /*
168 We use a dirty hack here to disable all application windows (which we
169 must do because otherwise the calls to wxYield() could lead to some very
170 unexpected reentrancies in the users code) but to avoid losing
171 focus/activation entirely when the child process terminates which would
172 happen if we simply disabled everything using wxWindowDisabler. Indeed,
173 remember that Windows will never activate a disabled window and when the
174 last child's window is closed and Windows looks for a window to activate
175 all our windows are still disabled. There is no way to enable them in
176 time because we don't know when the child's windows are going to be
177 closed, so the solution we use here is to keep one special tiny dialog
178 enabled all the time. Then when the child terminates it will get
179 activated and when we close it below -- after re-enabling all the other
180 windows! -- the previously active window becomes activated again and
181 everything is ok.
182 */
183 wxBeginBusyCursor();
184
185 // first disable all existing windows
186 wxWindowDisabler *wd = new wxWindowDisabler;
187
188 // then create an "invisible" dialog: it has minimal size, is positioned
189 // (hopefully) outside the screen and doesn't appear in the Alt-TAB list
190 // (unlike the frames, which is why we use a dialog here)
191 wxWindow *winActive = new wxDialog
192 (
193 wxTheApp->GetTopWindow(),
194 wxID_ANY,
195 wxEmptyString,
196 wxPoint(32600, 32600),
197 wxSize(1, 1)
198 );
199 winActive->Show();
200
201 return new ChildWaitLoopData(wd, winActive);
202 }
203
204 void wxGUIAppTraits::AfterChildWaitLoop(void *dataOrig)
205 {
206 wxEndBusyCursor();
207
208 ChildWaitLoopData * const data = (ChildWaitLoopData *)dataOrig;
209
210 delete data->wd;
211
212 // finally delete the dummy dialog and, as wd has been already destroyed
213 // and the other windows reenabled, the activation is going to return to
214 // the window which had had it before
215 data->winActive->Destroy();
216
217 // also delete the temporary data object itself
218 delete data;
219 }
220
221 #if wxUSE_THREADS
222 bool wxGUIAppTraits::DoMessageFromThreadWait()
223 {
224 // we should return false only if the app should exit, i.e. only if
225 // Dispatch() determines that the main event loop should terminate
226 wxEventLoopBase * const evtLoop = wxEventLoop::GetActive();
227 if ( !evtLoop || !evtLoop->Pending() )
228 {
229 // no events means no quit event
230 return true;
231 }
232
233 return evtLoop->Dispatch();
234 }
235
236 DWORD wxGUIAppTraits::WaitForThread(WXHANDLE hThread, int flags)
237 {
238 // We only ever dispatch messages from the main thread and, additionally,
239 // even from the main thread we shouldn't wait for the message if we don't
240 // have a running event loop as we would never remove them from the message
241 // queue then and so we would enter an infinite loop as
242 // MsgWaitForMultipleObjects() keeps returning WAIT_OBJECT_0 + 1.
243 if ( flags == wxTHREAD_WAIT_BLOCK ||
244 !wxIsMainThread() ||
245 !wxEventLoop::GetActive() )
246 {
247 // Simple blocking wait.
248 return DoSimpleWaitForThread(hThread);
249 }
250
251 return ::MsgWaitForMultipleObjects
252 (
253 1, // number of objects to wait for
254 (HANDLE *)&hThread, // the objects
255 false, // wait for any objects, not all
256 INFINITE, // no timeout
257 QS_ALLINPUT | // return as soon as there are any events
258 QS_ALLPOSTMESSAGE
259 );
260 }
261 #endif // wxUSE_THREADS
262
263 wxPortId wxGUIAppTraits::GetToolkitVersion(int *majVer, int *minVer) const
264 {
265 OSVERSIONINFO info;
266 wxZeroMemory(info);
267
268 // on Windows, the toolkit version is the same of the OS version
269 // as Windows integrates the OS kernel with the GUI toolkit.
270 info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
271 if ( ::GetVersionEx(&info) )
272 {
273 if ( majVer )
274 *majVer = info.dwMajorVersion;
275 if ( minVer )
276 *minVer = info.dwMinorVersion;
277 }
278
279 #if defined(__WXHANDHELD__) || defined(__WXWINCE__)
280 return wxPORT_WINCE;
281 #else
282 return wxPORT_MSW;
283 #endif
284 }
285
286 #if wxUSE_TIMER
287
288 wxTimerImpl *wxGUIAppTraits::CreateTimerImpl(wxTimer *timer)
289 {
290 return new wxMSWTimerImpl(timer);
291 }
292
293 #endif // wxUSE_TIMER
294
295 wxEventLoopBase* wxGUIAppTraits::CreateEventLoop()
296 {
297 return new wxEventLoop;
298 }
299
300 // ---------------------------------------------------------------------------
301 // Stuff for using console from the GUI applications
302 // ---------------------------------------------------------------------------
303
304 #ifndef __WXWINCE__
305
306 #if wxUSE_DYNLIB_CLASS
307
308 #include <wx/dynlib.h>
309
310 namespace
311 {
312
313 /*
314 Helper class to manipulate console from a GUI app.
315
316 Notice that console output is available in the GUI app only if:
317 - AttachConsole() returns TRUE (which means it never works under pre-XP)
318 - we have a valid STD_ERROR_HANDLE
319 - command history hasn't been changed since our startup
320
321 To check if all these conditions are verified, you need to simple call
322 IsOkToUse(). It will check the first two conditions above the first time it
323 is called (and if this fails, the subsequent calls will return immediately)
324 and also recheck the last one every time it is called.
325 */
326 class wxConsoleStderr
327 {
328 public:
329 // default ctor does nothing, call Init() before using this class
330 wxConsoleStderr()
331 {
332 m_hStderr = INVALID_HANDLE_VALUE;
333 m_historyLen =
334 m_dataLen =
335 m_dataLine = 0;
336
337 m_ok = -1;
338 }
339
340 ~wxConsoleStderr()
341 {
342 if ( m_hStderr != INVALID_HANDLE_VALUE )
343 {
344 if ( !::FreeConsole() )
345 {
346 wxLogLastError(wxT("FreeConsole"));
347 }
348 }
349 }
350
351 // return true if we were successfully initialized and there had been no
352 // console activity which would interfere with our output since then
353 bool IsOkToUse() const
354 {
355 if ( m_ok == -1 )
356 {
357 wxConsoleStderr * const self = const_cast<wxConsoleStderr *>(this);
358 self->m_ok = self->DoInit();
359
360 // no need to call IsHistoryUnchanged() as we just initialized
361 // m_history anyhow
362 return m_ok == 1;
363 }
364
365 return m_ok && IsHistoryUnchanged();
366 }
367
368
369 // output the provided text on the console, return true if ok
370 bool Write(const wxString& text);
371
372 private:
373 // called by Init() once only to do the real initialization
374 bool DoInit();
375
376 // retrieve the command line history into the provided buffer and return
377 // its length
378 int GetCommandHistory(wxWxCharBuffer& buf) const;
379
380 // check if the console history has changed
381 bool IsHistoryUnchanged() const;
382
383 int m_ok; // initially -1, set to true or false by Init()
384
385 wxDynamicLibrary m_dllKernel32;
386
387 HANDLE m_hStderr; // console handle, if it's valid we must call
388 // FreeConsole() (even if m_ok != 1)
389
390 wxWxCharBuffer m_history; // command history on startup
391 int m_historyLen; // length command history buffer
392
393 wxCharBuffer m_data; // data between empty line and cursor position
394 int m_dataLen; // length data buffer
395 int m_dataLine; // line offset
396
397 typedef DWORD (WINAPI *GetConsoleCommandHistory_t)(LPTSTR sCommands,
398 DWORD nBufferLength,
399 LPCTSTR sExeName);
400 typedef DWORD (WINAPI *GetConsoleCommandHistoryLength_t)(LPCTSTR sExeName);
401
402 GetConsoleCommandHistory_t m_pfnGetConsoleCommandHistory;
403 GetConsoleCommandHistoryLength_t m_pfnGetConsoleCommandHistoryLength;
404
405 wxDECLARE_NO_COPY_CLASS(wxConsoleStderr);
406 };
407
408 bool wxConsoleStderr::DoInit()
409 {
410 HANDLE hStderr = ::GetStdHandle(STD_ERROR_HANDLE);
411
412 if ( hStderr == INVALID_HANDLE_VALUE || !hStderr )
413 return false;
414
415 if ( !m_dllKernel32.Load(wxT("kernel32.dll")) )
416 return false;
417
418 typedef BOOL (WINAPI *AttachConsole_t)(DWORD dwProcessId);
419 AttachConsole_t wxDL_INIT_FUNC(pfn, AttachConsole, m_dllKernel32);
420
421 if ( !pfnAttachConsole || !pfnAttachConsole(ATTACH_PARENT_PROCESS) )
422 return false;
423
424 // console attached, set m_hStderr now to ensure that we free it in the
425 // dtor
426 m_hStderr = hStderr;
427
428 wxDL_INIT_FUNC_AW(m_pfn, GetConsoleCommandHistory, m_dllKernel32);
429 if ( !m_pfnGetConsoleCommandHistory )
430 return false;
431
432 wxDL_INIT_FUNC_AW(m_pfn, GetConsoleCommandHistoryLength, m_dllKernel32);
433 if ( !m_pfnGetConsoleCommandHistoryLength )
434 return false;
435
436 // remember the current command history to be able to compare with it later
437 // in IsHistoryUnchanged()
438 m_historyLen = GetCommandHistory(m_history);
439 if ( !m_history )
440 return false;
441
442
443 // now find the first blank line above the current position
444 CONSOLE_SCREEN_BUFFER_INFO csbi;
445
446 if ( !::GetConsoleScreenBufferInfo(m_hStderr, &csbi) )
447 {
448 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
449 return false;
450 }
451
452 COORD pos;
453 pos.X = 0;
454 pos.Y = csbi.dwCursorPosition.Y + 1;
455
456 // we decide that a line is empty if first 4 characters are spaces
457 DWORD ret;
458 char buf[4];
459 do
460 {
461 pos.Y--;
462 if ( !::ReadConsoleOutputCharacterA(m_hStderr, buf, WXSIZEOF(buf),
463 pos, &ret) )
464 {
465 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
466 return false;
467 }
468 } while ( wxStrncmp(" ", buf, WXSIZEOF(buf)) != 0 );
469
470 // calculate line offset and length of data
471 m_dataLine = csbi.dwCursorPosition.Y - pos.Y;
472 m_dataLen = m_dataLine*csbi.dwMaximumWindowSize.X + csbi.dwCursorPosition.X;
473
474 if ( m_dataLen > 0 )
475 {
476 m_data.extend(m_dataLen);
477 if ( !::ReadConsoleOutputCharacterA(m_hStderr, m_data.data(), m_dataLen,
478 pos, &ret) )
479 {
480 wxLogLastError(wxT("ReadConsoleOutputCharacterA"));
481 return false;
482 }
483 }
484
485 return true;
486 }
487
488 int wxConsoleStderr::GetCommandHistory(wxWxCharBuffer& buf) const
489 {
490 // these functions are internal and may only be called by cmd.exe
491 static const wxChar *CMD_EXE = wxT("cmd.exe");
492
493 const int len = m_pfnGetConsoleCommandHistoryLength(CMD_EXE);
494 if ( len )
495 {
496 buf.extend(len);
497
498 int len2 = m_pfnGetConsoleCommandHistory(buf.data(), len, CMD_EXE);
499
500 #if !wxUSE_UNICODE
501 // there seems to be a bug in the GetConsoleCommandHistoryA(), it
502 // returns the length of Unicode string and not ANSI one
503 len2 /= 2;
504 #endif // !wxUSE_UNICODE
505
506 if ( len2 != len )
507 {
508 wxFAIL_MSG( wxT("failed getting history?") );
509 }
510 }
511
512 return len;
513 }
514
515 bool wxConsoleStderr::IsHistoryUnchanged() const
516 {
517 wxASSERT_MSG( m_ok == 1, wxT("shouldn't be called if not initialized") );
518
519 // get (possibly changed) command history
520 wxWxCharBuffer history;
521 const int historyLen = GetCommandHistory(history);
522
523 // and compare it with the original one
524 return historyLen == m_historyLen && history &&
525 memcmp(m_history, history, historyLen) == 0;
526 }
527
528 bool wxConsoleStderr::Write(const wxString& text)
529 {
530 wxASSERT_MSG( m_hStderr != INVALID_HANDLE_VALUE,
531 wxT("should only be called if Init() returned true") );
532
533 // get current position
534 CONSOLE_SCREEN_BUFFER_INFO csbi;
535 if ( !::GetConsoleScreenBufferInfo(m_hStderr, &csbi) )
536 {
537 wxLogLastError(wxT("GetConsoleScreenBufferInfo"));
538 return false;
539 }
540
541 // and calculate new position (where is empty line)
542 csbi.dwCursorPosition.X = 0;
543 csbi.dwCursorPosition.Y -= m_dataLine;
544
545 if ( !::SetConsoleCursorPosition(m_hStderr, csbi.dwCursorPosition) )
546 {
547 wxLogLastError(wxT("SetConsoleCursorPosition"));
548 return false;
549 }
550
551 DWORD ret;
552 if ( !::FillConsoleOutputCharacter(m_hStderr, wxT(' '), m_dataLen,
553 csbi.dwCursorPosition, &ret) )
554 {
555 wxLogLastError(wxT("FillConsoleOutputCharacter"));
556 return false;
557 }
558
559 if ( !::WriteConsole(m_hStderr, text.t_str(), text.length(), &ret, NULL) )
560 {
561 wxLogLastError(wxT("WriteConsole"));
562 return false;
563 }
564
565 WriteConsoleA(m_hStderr, m_data, m_dataLen, &ret, 0);
566
567 return true;
568 }
569
570 wxConsoleStderr s_consoleStderr;
571
572 } // anonymous namespace
573
574 bool wxGUIAppTraits::CanUseStderr()
575 {
576 return s_consoleStderr.IsOkToUse();
577 }
578
579 bool wxGUIAppTraits::WriteToStderr(const wxString& text)
580 {
581 return s_consoleStderr.IsOkToUse() && s_consoleStderr.Write(text);
582 }
583
584 #else // !wxUSE_DYNLIB_CLASS
585
586 bool wxGUIAppTraits::CanUseStderr()
587 {
588 return false;
589 }
590
591 bool wxGUIAppTraits::WriteToStderr(const wxString& WXUNUSED(text))
592 {
593 return false;
594 }
595
596 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
597
598 #endif // !__WXWINCE__
599
600 // ===========================================================================
601 // wxApp implementation
602 // ===========================================================================
603
604 int wxApp::m_nCmdShow = SW_SHOWNORMAL;
605
606 // ---------------------------------------------------------------------------
607 // wxWin macros
608 // ---------------------------------------------------------------------------
609
610 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
611
612 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
613 EVT_IDLE(wxApp::OnIdle)
614 EVT_END_SESSION(wxApp::OnEndSession)
615 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
616 END_EVENT_TABLE()
617
618 // class to ensure that wxAppBase::CleanUp() is called if our Initialize()
619 // fails
620 class wxCallBaseCleanup
621 {
622 public:
623 wxCallBaseCleanup(wxApp *app) : m_app(app) { }
624 ~wxCallBaseCleanup() { if ( m_app ) m_app->wxAppBase::CleanUp(); }
625
626 void Dismiss() { m_app = NULL; }
627
628 private:
629 wxApp *m_app;
630 };
631
632 //// Initialize
633 bool wxApp::Initialize(int& argc, wxChar **argv)
634 {
635 if ( !wxAppBase::Initialize(argc, argv) )
636 return false;
637
638 // ensure that base cleanup is done if we return too early
639 wxCallBaseCleanup callBaseCleanup(this);
640
641 #if !defined(__WXMICROWIN__)
642 InitCommonControls();
643 #endif // !defined(__WXMICROWIN__)
644
645 #if defined(__SMARTPHONE__) || defined(__POCKETPC__)
646 SHInitExtraControls();
647 #endif
648
649 #ifndef __WXWINCE__
650 // Don't show a message box if a function such as SHGetFileInfo
651 // fails to find a device.
652 SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
653 #endif
654
655 wxOleInitialize();
656
657 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
658 wxSetKeyboardHook(true);
659 #endif
660
661 callBaseCleanup.Dismiss();
662
663 return true;
664 }
665
666 // ---------------------------------------------------------------------------
667 // Win32 window class registration
668 // ---------------------------------------------------------------------------
669
670 /* static */
671 const wxChar *wxApp::GetRegisteredClassName(const wxChar *name,
672 int bgBrushCol,
673 int extraStyles)
674 {
675 const size_t count = gs_regClassesInfo.size();
676 for ( size_t n = 0; n < count; n++ )
677 {
678 if ( gs_regClassesInfo[n].regname == name )
679 return gs_regClassesInfo[n].regname.c_str();
680 }
681
682 // we need to register this class
683 WNDCLASS wndclass;
684 wxZeroMemory(wndclass);
685
686 wndclass.lpfnWndProc = (WNDPROC)wxWndProc;
687 wndclass.hInstance = wxGetInstance();
688 wndclass.hCursor = ::LoadCursor(NULL, IDC_ARROW);
689 wndclass.hbrBackground = (HBRUSH)wxUIntToPtr(bgBrushCol + 1);
690 wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | extraStyles;
691
692
693 ClassRegInfo regClass(name);
694 wndclass.lpszClassName = regClass.regname.t_str();
695 if ( !::RegisterClass(&wndclass) )
696 {
697 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
698 regClass.regname));
699 return NULL;
700 }
701
702 wndclass.style &= ~(CS_HREDRAW | CS_VREDRAW);
703 wndclass.lpszClassName = regClass.regnameNR.t_str();
704 if ( !::RegisterClass(&wndclass) )
705 {
706 wxLogLastError(wxString::Format(wxT("RegisterClass(%s)"),
707 regClass.regname));
708 ::UnregisterClass(regClass.regname.c_str(), wxGetInstance());
709 return NULL;
710 }
711
712 gs_regClassesInfo.push_back(regClass);
713
714 // take care to return the pointer which will remain valid after the
715 // function returns (it could be invalidated later if new elements are
716 // added to the vector and it's reallocated but this shouldn't matter as
717 // this pointer should be used right now, not stored)
718 return gs_regClassesInfo.back().regname.t_str();
719 }
720
721 bool wxApp::IsRegisteredClassName(const wxString& name)
722 {
723 const size_t count = gs_regClassesInfo.size();
724 for ( size_t n = 0; n < count; n++ )
725 {
726 if ( gs_regClassesInfo[n].regname == name ||
727 gs_regClassesInfo[n].regnameNR == name )
728 return true;
729 }
730
731 return false;
732 }
733
734 void wxApp::UnregisterWindowClasses()
735 {
736 const size_t count = gs_regClassesInfo.size();
737 for ( size_t n = 0; n < count; n++ )
738 {
739 const ClassRegInfo& regClass = gs_regClassesInfo[n];
740 if ( !::UnregisterClass(regClass.regname.c_str(), wxGetInstance()) )
741 {
742 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
743 regClass.regname));
744 }
745
746 if ( !::UnregisterClass(regClass.regnameNR.c_str(), wxGetInstance()) )
747 {
748 wxLogLastError(wxString::Format(wxT("UnregisterClass(%s)"),
749 regClass.regnameNR));
750 }
751 }
752
753 gs_regClassesInfo.clear();
754 }
755
756 void wxApp::CleanUp()
757 {
758 // all objects pending for deletion must be deleted first, otherwise
759 // UnregisterWindowClasses() call wouldn't succeed (because windows
760 // using the classes being unregistered still exist), so call the base
761 // class method first and only then do our clean up
762 wxAppBase::CleanUp();
763
764 #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
765 wxSetKeyboardHook(false);
766 #endif
767
768 wxOleUninitialize();
769
770 // for an EXE the classes are unregistered when it terminates but DLL may
771 // be loaded several times (load/unload/load) into the same process in
772 // which case the registration will fail after the first time if we don't
773 // unregister the classes now
774 UnregisterWindowClasses();
775 }
776
777 // ----------------------------------------------------------------------------
778 // wxApp ctor/dtor
779 // ----------------------------------------------------------------------------
780
781 wxApp::wxApp()
782 {
783 m_printMode = wxPRINT_WINDOWS;
784 }
785
786 wxApp::~wxApp()
787 {
788 }
789
790 // ----------------------------------------------------------------------------
791 // wxApp idle handling
792 // ----------------------------------------------------------------------------
793
794 void wxApp::OnIdle(wxIdleEvent& WXUNUSED(event))
795 {
796 #if wxUSE_DC_CACHEING
797 // automated DC cache management: clear the cached DCs and bitmap
798 // if it's likely that the app has finished with them, that is, we
799 // get an idle event and we're not dragging anything.
800 if (!::GetKeyState(MK_LBUTTON) && !::GetKeyState(MK_MBUTTON) && !::GetKeyState(MK_RBUTTON))
801 wxMSWDCImpl::ClearCache();
802 #endif // wxUSE_DC_CACHEING
803 }
804
805 void wxApp::WakeUpIdle()
806 {
807 // Send the top window a dummy message so idle handler processing will
808 // start up again. Doing it this way ensures that the idle handler
809 // wakes up in the right thread (see also wxWakeUpMainThread() which does
810 // the same for the main app thread only)
811 wxWindow * const topWindow = wxTheApp->GetTopWindow();
812 if ( topWindow )
813 {
814 HWND hwndTop = GetHwndOf(topWindow);
815
816 // Do not post WM_NULL if there's already a pending WM_NULL to avoid
817 // overflowing the message queue.
818 //
819 // Notice that due to a limitation of PeekMessage() API (which handles
820 // 0,0 range specially), we have to check the range from 0-1 instead.
821 // This still makes it possible to overflow the queue with WM_NULLs by
822 // interspersing the calles to WakeUpIdle() with windows creation but
823 // it should be rather hard to do it accidentally.
824 MSG msg;
825 if ( !::PeekMessage(&msg, hwndTop, 0, 1, PM_NOREMOVE) ||
826 ::PeekMessage(&msg, hwndTop, 1, 1, PM_NOREMOVE) )
827 {
828 if ( !::PostMessage(hwndTop, WM_NULL, 0, 0) )
829 {
830 // should never happen
831 wxLogLastError(wxT("PostMessage(WM_NULL)"));
832 }
833 }
834 }
835 #if wxUSE_THREADS
836 else
837 wxWakeUpMainThread();
838 #endif // wxUSE_THREADS
839 }
840
841 // ----------------------------------------------------------------------------
842 // other wxApp event hanlders
843 // ----------------------------------------------------------------------------
844
845 void wxApp::OnEndSession(wxCloseEvent& WXUNUSED(event))
846 {
847 // Windows will terminate the process soon after we return from
848 // WM_ENDSESSION handler or when we delete our last window, so make sure we
849 // at least execute our cleanup code before
850
851 // prevent the window from being destroyed when the corresponding wxTLW is
852 // destroyed: this will result in a leak of a HWND, of course, but who
853 // cares when the process is being killed anyhow
854 if ( !wxTopLevelWindows.empty() )
855 wxTopLevelWindows[0]->SetHWND(0);
856
857 const int rc = OnExit();
858
859 wxEntryCleanup();
860
861 // calling exit() instead of ExitProcess() or not doing anything at all and
862 // being killed by Windows has the advantage of executing the dtors of
863 // global objects
864 exit(rc);
865 }
866
867 // Default behaviour: close the application with prompts. The
868 // user can veto the close, and therefore the end session.
869 void wxApp::OnQueryEndSession(wxCloseEvent& event)
870 {
871 if (GetTopWindow())
872 {
873 if (!GetTopWindow()->Close(!event.CanVeto()))
874 event.Veto(true);
875 }
876 }
877
878 // ----------------------------------------------------------------------------
879 // system DLL versions
880 // ----------------------------------------------------------------------------
881
882 // these functions have trivial inline implementations for CE
883 #ifndef __WXWINCE__
884
885 #if wxUSE_DYNLIB_CLASS
886
887 namespace
888 {
889
890 // helper function: retrieve the DLL version by using DllGetVersion(), returns
891 // 0 if the DLL doesn't export such function
892 int CallDllGetVersion(wxDynamicLibrary& dll)
893 {
894 // now check if the function is available during run-time
895 wxDYNLIB_FUNCTION( DLLGETVERSIONPROC, DllGetVersion, dll );
896 if ( !pfnDllGetVersion )
897 return 0;
898
899 DLLVERSIONINFO dvi;
900 dvi.cbSize = sizeof(dvi);
901
902 HRESULT hr = (*pfnDllGetVersion)(&dvi);
903 if ( FAILED(hr) )
904 {
905 wxLogApiError(wxT("DllGetVersion"), hr);
906
907 return 0;
908 }
909
910 return 100*dvi.dwMajorVersion + dvi.dwMinorVersion;
911 }
912
913 } // anonymous namespace
914
915 /* static */
916 int wxApp::GetComCtl32Version()
917 {
918 // cache the result
919 //
920 // NB: this is MT-ok as in the worst case we'd compute s_verComCtl32 twice,
921 // but as its value should be the same both times it doesn't matter
922 static int s_verComCtl32 = -1;
923
924 if ( s_verComCtl32 == -1 )
925 {
926 // we're prepared to handle the errors
927 wxLogNull noLog;
928
929 // we don't want to load comctl32.dll, it should be already loaded but,
930 // depending on the OS version and the presence of the manifest, it can
931 // be either v5 or v6 and instead of trying to guess it just get the
932 // handle of the already loaded version
933 wxLoadedDLL dllComCtl32(wxT("comctl32.dll"));
934 if ( !dllComCtl32.IsLoaded() )
935 {
936 s_verComCtl32 = 0;
937 return 0;
938 }
939
940 // try DllGetVersion() for recent DLLs
941 s_verComCtl32 = CallDllGetVersion(dllComCtl32);
942
943 // if DllGetVersion() is unavailable either during compile or
944 // run-time, try to guess the version otherwise
945 if ( !s_verComCtl32 )
946 {
947 // InitCommonControlsEx is unique to 4.70 and later
948 void *pfn = dllComCtl32.GetSymbol(wxT("InitCommonControlsEx"));
949 if ( !pfn )
950 {
951 // not found, must be 4.00
952 s_verComCtl32 = 400;
953 }
954 else // 4.70+
955 {
956 // many symbols appeared in comctl32 4.71, could use any of
957 // them except may be DllInstall()
958 pfn = dllComCtl32.GetSymbol(wxT("InitializeFlatSB"));
959 if ( !pfn )
960 {
961 // not found, must be 4.70
962 s_verComCtl32 = 470;
963 }
964 else
965 {
966 // found, must be 4.71 or later
967 s_verComCtl32 = 471;
968 }
969 }
970 }
971 }
972
973 return s_verComCtl32;
974 }
975
976 /* static */
977 int wxApp::GetShell32Version()
978 {
979 static int s_verShell32 = -1;
980 if ( s_verShell32 == -1 )
981 {
982 // we're prepared to handle the errors
983 wxLogNull noLog;
984
985 wxDynamicLibrary dllShell32(wxT("shell32.dll"), wxDL_VERBATIM);
986 if ( dllShell32.IsLoaded() )
987 {
988 s_verShell32 = CallDllGetVersion(dllShell32);
989
990 if ( !s_verShell32 )
991 {
992 // there doesn't seem to be any way to distinguish between 4.00
993 // and 4.70 (starting from 4.71 we have DllGetVersion()) so
994 // just assume it is 4.0
995 s_verShell32 = 400;
996 }
997 }
998 else // failed load the DLL?
999 {
1000 s_verShell32 = 0;
1001 }
1002 }
1003
1004 return s_verShell32;
1005 }
1006
1007 #else // !wxUSE_DYNLIB_CLASS
1008
1009 /* static */
1010 int wxApp::GetComCtl32Version()
1011 {
1012 return 0;
1013 }
1014
1015 /* static */
1016 int wxApp::GetShell32Version()
1017 {
1018 return 0;
1019 }
1020
1021 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
1022
1023 #endif // !__WXWINCE__
1024
1025 #if wxUSE_EXCEPTIONS
1026
1027 // ----------------------------------------------------------------------------
1028 // exception handling
1029 // ----------------------------------------------------------------------------
1030
1031 bool wxApp::OnExceptionInMainLoop()
1032 {
1033 // ask the user about what to do: use the Win32 API function here as it
1034 // could be dangerous to use any wxWidgets code in this state
1035 switch (
1036 ::MessageBox
1037 (
1038 NULL,
1039 wxT("An unhandled exception occurred. Press \"Abort\" to \
1040 terminate the program,\r\n\
1041 \"Retry\" to exit the program normally and \"Ignore\" to try to continue."),
1042 wxT("Unhandled exception"),
1043 MB_ABORTRETRYIGNORE |
1044 MB_ICONERROR|
1045 MB_TASKMODAL
1046 )
1047 )
1048 {
1049 case IDABORT:
1050 throw;
1051
1052 default:
1053 wxFAIL_MSG( wxT("unexpected MessageBox() return code") );
1054 // fall through
1055
1056 case IDRETRY:
1057 return false;
1058
1059 case IDIGNORE:
1060 return true;
1061 }
1062 }
1063
1064 #endif // wxUSE_EXCEPTIONS