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