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