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