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