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