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