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