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