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