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