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