Add component-level filtering to wxLog.
[wxWidgets.git] / src / common / log.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/log.cpp
3 // Purpose: Assorted wxLogXXX functions, and wxLog (sink for logs)
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 29/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #if wxUSE_LOG
28
29 // wxWidgets
30 #ifndef WX_PRECOMP
31 #include "wx/log.h"
32 #include "wx/app.h"
33 #include "wx/arrstr.h"
34 #include "wx/intl.h"
35 #include "wx/string.h"
36 #include "wx/utils.h"
37 #endif //WX_PRECOMP
38
39 #include "wx/apptrait.h"
40 #include "wx/datetime.h"
41 #include "wx/file.h"
42 #include "wx/msgout.h"
43 #include "wx/textfile.h"
44 #include "wx/thread.h"
45 #include "wx/crt.h"
46
47 // other standard headers
48 #ifndef __WXWINCE__
49 #include <errno.h>
50 #endif
51
52 #include <stdlib.h>
53
54 #ifndef __WXPALMOS5__
55 #ifndef __WXWINCE__
56 #include <time.h>
57 #else
58 #include "wx/msw/wince/time.h"
59 #endif
60 #endif /* ! __WXPALMOS5__ */
61
62 #if defined(__WINDOWS__)
63 #include "wx/msw/private.h" // includes windows.h
64 #endif
65
66 #undef wxLOG_COMPONENT
67 const char *wxLOG_COMPONENT = "";
68
69 #if wxUSE_THREADS
70
71 // define static functions providing access to the critical sections we use
72 // instead of just using static critical section variables as log functions may
73 // be used during static initialization and while this is certainly not
74 // advisable it's still better to not crash (as we'd do if we used a yet
75 // uninitialized critical section) if it happens
76
77 static inline wxCriticalSection& GetTraceMaskCS()
78 {
79 static wxCriticalSection s_csTrace;
80
81 return s_csTrace;
82 }
83
84 static inline wxCriticalSection& GetPreviousLogCS()
85 {
86 static wxCriticalSection s_csPrev;
87
88 return s_csPrev;
89 }
90
91 static inline wxCriticalSection& GetLevelsCS()
92 {
93 static wxCriticalSection s_csLevels;
94
95 return s_csLevels;
96 }
97
98 #endif // wxUSE_THREADS
99
100 // ----------------------------------------------------------------------------
101 // non member functions
102 // ----------------------------------------------------------------------------
103
104 // define this to enable wrapping of log messages
105 //#define LOG_PRETTY_WRAP
106
107 #ifdef LOG_PRETTY_WRAP
108 static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz);
109 #endif
110
111 // ----------------------------------------------------------------------------
112 // module globals
113 // ----------------------------------------------------------------------------
114
115 namespace
116 {
117
118 // this struct is used to store information about the previous log message used
119 // by OnLog() to (optionally) avoid logging multiple copies of the same message
120 struct PreviousLogInfo
121 {
122 PreviousLogInfo()
123 {
124 numRepeated = 0;
125 }
126
127
128 // previous message itself
129 wxString msg;
130
131 // its level
132 wxLogLevel level;
133
134 // other information about it
135 wxLogRecordInfo info;
136
137 // the number of times it was already repeated
138 unsigned numRepeated;
139 };
140
141 PreviousLogInfo gs_prevLog;
142
143
144 // map containing all components for which log level was explicitly set
145 //
146 // NB: all accesses to it must be protected by GetLevelsCS() critical section
147 wxStringToNumHashMap gs_componentLevels;
148
149 } // anonymous namespace
150
151 // ============================================================================
152 // implementation
153 // ============================================================================
154
155 // ----------------------------------------------------------------------------
156 // helper global functions
157 // ----------------------------------------------------------------------------
158
159 void wxSafeShowMessage(const wxString& title, const wxString& text)
160 {
161 #ifdef __WINDOWS__
162 ::MessageBox(NULL, text.t_str(), title.t_str(), MB_OK | MB_ICONSTOP);
163 #else
164 wxFprintf(stderr, wxS("%s: %s\n"), title.c_str(), text.c_str());
165 fflush(stderr);
166 #endif
167 }
168
169 // ----------------------------------------------------------------------------
170 // wxLog class implementation
171 // ----------------------------------------------------------------------------
172
173 unsigned wxLog::LogLastRepeatIfNeeded()
174 {
175 wxCRIT_SECT_LOCKER(lock, GetPreviousLogCS());
176
177 return LogLastRepeatIfNeededUnlocked();
178 }
179
180 unsigned wxLog::LogLastRepeatIfNeededUnlocked()
181 {
182 const unsigned count = gs_prevLog.numRepeated;
183
184 if ( gs_prevLog.numRepeated )
185 {
186 wxString msg;
187 #if wxUSE_INTL
188 msg.Printf(wxPLURAL("The previous message repeated once.",
189 "The previous message repeated %lu times.",
190 gs_prevLog.numRepeated),
191 gs_prevLog.numRepeated);
192 #else
193 msg.Printf(wxS("The previous message was repeated %lu times."),
194 gs_prevLog.numRepeated);
195 #endif
196 gs_prevLog.numRepeated = 0;
197 gs_prevLog.msg.clear();
198 DoLogRecord(gs_prevLog.level, msg, gs_prevLog.info);
199 }
200
201 return count;
202 }
203
204 wxLog::~wxLog()
205 {
206 // Flush() must be called before destroying the object as otherwise some
207 // messages could be lost
208 if ( gs_prevLog.numRepeated )
209 {
210 wxMessageOutputDebug().Printf
211 (
212 wxS("Last repeated message (\"%s\", %lu times) wasn't output"),
213 gs_prevLog.msg,
214 gs_prevLog.numRepeated
215 );
216 }
217 }
218
219 // ----------------------------------------------------------------------------
220 // wxLog logging functions
221 // ----------------------------------------------------------------------------
222
223 /* static */
224 void
225 wxLog::OnLog(wxLogLevel level, const wxString& msg, time_t t)
226 {
227 wxLogRecordInfo info;
228 info.timestamp = t;
229 #if wxUSE_THREADS
230 info.threadId = wxThread::GetCurrentId();
231 #endif // wxUSE_THREADS
232
233 OnLog(level, msg, info);
234 }
235
236 /* static */
237 void
238 wxLog::OnLog(wxLogLevel level,
239 const wxString& msg,
240 const wxLogRecordInfo& info)
241 {
242 // fatal errors can't be suppressed nor handled by the custom log target
243 // and always terminate the program
244 if ( level == wxLOG_FatalError )
245 {
246 wxSafeShowMessage(wxS("Fatal Error"), msg);
247
248 #ifdef __WXWINCE__
249 ExitThread(3);
250 #else
251 abort();
252 #endif
253 }
254
255 wxLog *pLogger = GetActiveTarget();
256 if ( !pLogger )
257 return;
258
259 if ( GetRepetitionCounting() )
260 {
261 wxCRIT_SECT_LOCKER(lock, GetPreviousLogCS());
262
263 if ( msg == gs_prevLog.msg )
264 {
265 gs_prevLog.numRepeated++;
266
267 // nothing else to do, in particular, don't log the
268 // repeated message
269 return;
270 }
271
272 pLogger->LogLastRepeatIfNeededUnlocked();
273
274 // reset repetition counter for a new message
275 gs_prevLog.msg = msg;
276 gs_prevLog.level = level;
277 gs_prevLog.info = info;
278 }
279
280 // handle extra data which may be passed to us by wxLogXXX()
281 wxString prefix, suffix;
282 wxUIntPtr num = 0;
283 if ( info.GetNumValue(wxLOG_KEY_SYS_ERROR_CODE, &num) )
284 {
285 long err = static_cast<long>(num);
286 if ( !err )
287 err = wxSysErrorCode();
288
289 suffix.Printf(_(" (error %ld: %s)"), err, wxSysErrorMsg(err));
290 }
291
292 #if wxUSE_LOG_TRACE
293 wxString str;
294 if ( level == wxLOG_Trace && info.GetStrValue(wxLOG_KEY_TRACE_MASK, &str) )
295 {
296 prefix = "(" + str + ") ";
297 }
298 #endif // wxUSE_LOG_TRACE
299
300 pLogger->DoLogRecord(level, prefix + msg + suffix, info);
301 }
302
303 void wxLog::DoLogRecord(wxLogLevel level,
304 const wxString& msg,
305 const wxLogRecordInfo& info)
306 {
307 #if WXWIN_COMPATIBILITY_2_8
308 // call the old DoLog() to ensure that existing custom log classes still
309 // work
310 //
311 // as the user code could have defined it as either taking "const char *"
312 // (in ANSI build) or "const wxChar *" (in ANSI/Unicode), we have no choice
313 // but to call both of them
314 DoLog(level, (const char*)msg.mb_str(), info.timestamp);
315 DoLog(level, (const wchar_t*)msg.wc_str(), info.timestamp);
316 #endif // WXWIN_COMPATIBILITY_2_8
317
318
319 // TODO: it would be better to extract message formatting in a separate
320 // wxLogFormatter class but for now we hard code formatting here
321
322 wxString prefix;
323
324 // don't time stamp debug messages under MSW as debug viewers usually
325 // already have an option to do it
326 #ifdef __WXMSW__
327 if ( level != wxLOG_Debug && level != wxLOG_Trace )
328 #endif // __WXMSW__
329 TimeStamp(&prefix);
330
331 // TODO: use the other wxLogRecordInfo fields
332
333 switch ( level )
334 {
335 case wxLOG_Error:
336 prefix += _("Error: ");
337 break;
338
339 case wxLOG_Warning:
340 prefix += _("Warning: ");
341 break;
342
343 // don't prepend "debug/trace" prefix under MSW as it goes to the debug
344 // window anyhow and so can't be confused with something else
345 #ifndef __WXMSW__
346 case wxLOG_Debug:
347 // this prefix (as well as the one below) is intentionally not
348 // translated as nobody translates debug messages anyhow
349 prefix += "Debug: ";
350 break;
351
352 case wxLOG_Trace:
353 prefix += "Trace: ";
354 break;
355 #endif // !__WXMSW__
356 }
357
358 DoLogTextAtLevel(level, prefix + msg);
359 }
360
361 void wxLog::DoLogTextAtLevel(wxLogLevel level, const wxString& msg)
362 {
363 // we know about debug messages (because using wxMessageOutputDebug is the
364 // right thing to do in 99% of all cases and also for compatibility) but
365 // anything else needs to be handled in the derived class
366 if ( level == wxLOG_Debug || level == wxLOG_Trace )
367 {
368 wxMessageOutputDebug().Output(msg + wxS('\n'));
369 }
370 else
371 {
372 DoLogText(msg);
373 }
374 }
375
376 void wxLog::DoLogText(const wxString& WXUNUSED(msg))
377 {
378 // in 2.8-compatible build the derived class might override DoLog() or
379 // DoLogString() instead so we can't have this assert there
380 #if !WXWIN_COMPATIBILITY_2_8
381 wxFAIL_MSG( "must be overridden if it is called" );
382 #endif // WXWIN_COMPATIBILITY_2_8
383 }
384
385 #if WXWIN_COMPATIBILITY_2_8
386
387 void wxLog::DoLog(wxLogLevel WXUNUSED(level), const char *szString, time_t t)
388 {
389 DoLogString(szString, t);
390 }
391
392 void wxLog::DoLog(wxLogLevel WXUNUSED(level), const wchar_t *wzString, time_t t)
393 {
394 DoLogString(wzString, t);
395 }
396
397 #endif // WXWIN_COMPATIBILITY_2_8
398
399 // ----------------------------------------------------------------------------
400 // wxLog active target management
401 // ----------------------------------------------------------------------------
402
403 wxLog *wxLog::GetActiveTarget()
404 {
405 if ( ms_bAutoCreate && ms_pLogger == NULL ) {
406 // prevent infinite recursion if someone calls wxLogXXX() from
407 // wxApp::CreateLogTarget()
408 static bool s_bInGetActiveTarget = false;
409 if ( !s_bInGetActiveTarget ) {
410 s_bInGetActiveTarget = true;
411
412 // ask the application to create a log target for us
413 if ( wxTheApp != NULL )
414 ms_pLogger = wxTheApp->GetTraits()->CreateLogTarget();
415 else
416 ms_pLogger = new wxLogStderr;
417
418 s_bInGetActiveTarget = false;
419
420 // do nothing if it fails - what can we do?
421 }
422 }
423
424 return ms_pLogger;
425 }
426
427 wxLog *wxLog::SetActiveTarget(wxLog *pLogger)
428 {
429 if ( ms_pLogger != NULL ) {
430 // flush the old messages before changing because otherwise they might
431 // get lost later if this target is not restored
432 ms_pLogger->Flush();
433 }
434
435 wxLog *pOldLogger = ms_pLogger;
436 ms_pLogger = pLogger;
437
438 return pOldLogger;
439 }
440
441 void wxLog::DontCreateOnDemand()
442 {
443 ms_bAutoCreate = false;
444
445 // this is usually called at the end of the program and we assume that it
446 // is *always* called at the end - so we free memory here to avoid false
447 // memory leak reports from wxWin memory tracking code
448 ClearTraceMasks();
449 }
450
451 void wxLog::DoCreateOnDemand()
452 {
453 ms_bAutoCreate = true;
454 }
455
456 // ----------------------------------------------------------------------------
457 // wxLog components levels
458 // ----------------------------------------------------------------------------
459
460 /* static */
461 void wxLog::SetComponentLevel(const wxString& component, wxLogLevel level)
462 {
463 if ( component.empty() )
464 {
465 SetLogLevel(level);
466 }
467 else
468 {
469 wxCRIT_SECT_LOCKER(lock, GetLevelsCS());
470
471 gs_componentLevels[component] = level;
472 }
473 }
474
475 /* static */
476 wxLogLevel wxLog::GetComponentLevel(wxString component)
477 {
478 wxCRIT_SECT_LOCKER(lock, GetLevelsCS());
479
480 while ( !component.empty() )
481 {
482 wxStringToNumHashMap::const_iterator
483 it = gs_componentLevels.find(component);
484 if ( it != gs_componentLevels.end() )
485 return static_cast<wxLogLevel>(it->second);
486
487 component = component.BeforeLast('/');
488 }
489
490 return GetLogLevel();
491 }
492
493 // ----------------------------------------------------------------------------
494 // wxLog trace masks
495 // ----------------------------------------------------------------------------
496
497 void wxLog::AddTraceMask(const wxString& str)
498 {
499 wxCRIT_SECT_LOCKER(lock, GetTraceMaskCS());
500
501 ms_aTraceMasks.push_back(str);
502 }
503
504 void wxLog::RemoveTraceMask(const wxString& str)
505 {
506 wxCRIT_SECT_LOCKER(lock, GetTraceMaskCS());
507
508 int index = ms_aTraceMasks.Index(str);
509 if ( index != wxNOT_FOUND )
510 ms_aTraceMasks.RemoveAt((size_t)index);
511 }
512
513 void wxLog::ClearTraceMasks()
514 {
515 wxCRIT_SECT_LOCKER(lock, GetTraceMaskCS());
516
517 ms_aTraceMasks.Clear();
518 }
519
520 /*static*/ bool wxLog::IsAllowedTraceMask(const wxString& mask)
521 {
522 wxCRIT_SECT_LOCKER(lock, GetTraceMaskCS());
523
524 for ( wxArrayString::iterator it = ms_aTraceMasks.begin(),
525 en = ms_aTraceMasks.end();
526 it != en; ++it )
527 {
528 if ( *it == mask)
529 return true;
530 }
531
532 return false;
533 }
534
535 // ----------------------------------------------------------------------------
536 // wxLog miscellaneous other methods
537 // ----------------------------------------------------------------------------
538
539 void wxLog::TimeStamp(wxString *str)
540 {
541 #if wxUSE_DATETIME
542 if ( !ms_timestamp.empty() )
543 {
544 wxChar buf[256];
545 time_t timeNow;
546 (void)time(&timeNow);
547
548 struct tm tm;
549 wxStrftime(buf, WXSIZEOF(buf),
550 ms_timestamp, wxLocaltime_r(&timeNow, &tm));
551
552 str->Empty();
553 *str << buf << wxS(": ");
554 }
555 #endif // wxUSE_DATETIME
556 }
557
558 void wxLog::Flush()
559 {
560 LogLastRepeatIfNeeded();
561 }
562
563 // ----------------------------------------------------------------------------
564 // wxLogBuffer implementation
565 // ----------------------------------------------------------------------------
566
567 void wxLogBuffer::Flush()
568 {
569 if ( !m_str.empty() )
570 {
571 wxMessageOutputBest out;
572 out.Printf(wxS("%s"), m_str.c_str());
573 m_str.clear();
574 }
575 }
576
577 void wxLogBuffer::DoLogTextAtLevel(wxLogLevel level, const wxString& msg)
578 {
579 // don't put debug messages in the buffer, we don't want to show
580 // them to the user in a msg box, log them immediately
581 switch ( level )
582 {
583 case wxLOG_Debug:
584 case wxLOG_Trace:
585 wxLog::DoLogTextAtLevel(level, msg);
586 break;
587
588 default:
589 m_str << msg << wxS("\n");
590 }
591 }
592
593 // ----------------------------------------------------------------------------
594 // wxLogStderr class implementation
595 // ----------------------------------------------------------------------------
596
597 wxLogStderr::wxLogStderr(FILE *fp)
598 {
599 if ( fp == NULL )
600 m_fp = stderr;
601 else
602 m_fp = fp;
603 }
604
605 void wxLogStderr::DoLogText(const wxString& msg)
606 {
607 wxFputs(msg + '\n', m_fp);
608 fflush(m_fp);
609
610 // under GUI systems such as Windows or Mac, programs usually don't have
611 // stderr at all, so show the messages also somewhere else, typically in
612 // the debugger window so that they go at least somewhere instead of being
613 // simply lost
614 if ( m_fp == stderr )
615 {
616 wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
617 if ( traits && !traits->HasStderr() )
618 {
619 wxMessageOutputDebug().Output(msg + wxS('\n'));
620 }
621 }
622 }
623
624 // ----------------------------------------------------------------------------
625 // wxLogStream implementation
626 // ----------------------------------------------------------------------------
627
628 #if wxUSE_STD_IOSTREAM
629 #include "wx/ioswrap.h"
630 wxLogStream::wxLogStream(wxSTD ostream *ostr)
631 {
632 if ( ostr == NULL )
633 m_ostr = &wxSTD cerr;
634 else
635 m_ostr = ostr;
636 }
637
638 void wxLogStream::DoLogText(const wxString& msg)
639 {
640 (*m_ostr) << msg << wxSTD endl;
641 }
642 #endif // wxUSE_STD_IOSTREAM
643
644 // ----------------------------------------------------------------------------
645 // wxLogChain
646 // ----------------------------------------------------------------------------
647
648 wxLogChain::wxLogChain(wxLog *logger)
649 {
650 m_bPassMessages = true;
651
652 m_logNew = logger;
653 m_logOld = wxLog::SetActiveTarget(this);
654 }
655
656 wxLogChain::~wxLogChain()
657 {
658 delete m_logOld;
659
660 if ( m_logNew != this )
661 delete m_logNew;
662 }
663
664 void wxLogChain::SetLog(wxLog *logger)
665 {
666 if ( m_logNew != this )
667 delete m_logNew;
668
669 m_logNew = logger;
670 }
671
672 void wxLogChain::Flush()
673 {
674 if ( m_logOld )
675 m_logOld->Flush();
676
677 // be careful to avoid infinite recursion
678 if ( m_logNew && m_logNew != this )
679 m_logNew->Flush();
680 }
681
682 void wxLogChain::DoLogRecord(wxLogLevel level,
683 const wxString& msg,
684 const wxLogRecordInfo& info)
685 {
686 // let the previous logger show it
687 if ( m_logOld && IsPassingMessages() )
688 m_logOld->LogRecord(level, msg, info);
689
690 // and also send it to the new one
691 if ( m_logNew && m_logNew != this )
692 m_logNew->LogRecord(level, msg, info);
693 }
694
695 #ifdef __VISUALC__
696 // "'this' : used in base member initializer list" - so what?
697 #pragma warning(disable:4355)
698 #endif // VC++
699
700 // ----------------------------------------------------------------------------
701 // wxLogInterposer
702 // ----------------------------------------------------------------------------
703
704 wxLogInterposer::wxLogInterposer()
705 : wxLogChain(this)
706 {
707 }
708
709 // ----------------------------------------------------------------------------
710 // wxLogInterposerTemp
711 // ----------------------------------------------------------------------------
712
713 wxLogInterposerTemp::wxLogInterposerTemp()
714 : wxLogChain(this)
715 {
716 DetachOldLog();
717 }
718
719 #ifdef __VISUALC__
720 #pragma warning(default:4355)
721 #endif // VC++
722
723 // ============================================================================
724 // Global functions/variables
725 // ============================================================================
726
727 // ----------------------------------------------------------------------------
728 // static variables
729 // ----------------------------------------------------------------------------
730
731 bool wxLog::ms_bRepetCounting = false;
732
733 wxLog *wxLog::ms_pLogger = NULL;
734 bool wxLog::ms_doLog = true;
735 bool wxLog::ms_bAutoCreate = true;
736 bool wxLog::ms_bVerbose = false;
737
738 wxLogLevel wxLog::ms_logLevel = wxLOG_Max; // log everything by default
739
740 size_t wxLog::ms_suspendCount = 0;
741
742 wxString wxLog::ms_timestamp(wxS("%X")); // time only, no date
743
744 #if WXWIN_COMPATIBILITY_2_8
745 wxTraceMask wxLog::ms_ulTraceMask = (wxTraceMask)0;
746 #endif // wxDEBUG_LEVEL
747
748 wxArrayString wxLog::ms_aTraceMasks;
749
750 // ----------------------------------------------------------------------------
751 // stdout error logging helper
752 // ----------------------------------------------------------------------------
753
754 // helper function: wraps the message and justifies it under given position
755 // (looks more pretty on the terminal). Also adds newline at the end.
756 //
757 // TODO this is now disabled until I find a portable way of determining the
758 // terminal window size (ok, I found it but does anybody really cares?)
759 #ifdef LOG_PRETTY_WRAP
760 static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz)
761 {
762 size_t nMax = 80; // FIXME
763 size_t nStart = strlen(pszPrefix);
764 fputs(pszPrefix, f);
765
766 size_t n;
767 while ( *psz != '\0' ) {
768 for ( n = nStart; (n < nMax) && (*psz != '\0'); n++ )
769 putc(*psz++, f);
770
771 // wrapped?
772 if ( *psz != '\0' ) {
773 /*putc('\n', f);*/
774 for ( n = 0; n < nStart; n++ )
775 putc(' ', f);
776
777 // as we wrapped, squeeze all white space
778 while ( isspace(*psz) )
779 psz++;
780 }
781 }
782
783 putc('\n', f);
784 }
785 #endif //LOG_PRETTY_WRAP
786
787 // ----------------------------------------------------------------------------
788 // error code/error message retrieval functions
789 // ----------------------------------------------------------------------------
790
791 // get error code from syste
792 unsigned long wxSysErrorCode()
793 {
794 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
795 return ::GetLastError();
796 #else //Unix
797 return errno;
798 #endif //Win/Unix
799 }
800
801 // get error message from system
802 const wxChar *wxSysErrorMsg(unsigned long nErrCode)
803 {
804 if ( nErrCode == 0 )
805 nErrCode = wxSysErrorCode();
806
807 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
808 static wxChar s_szBuf[1024];
809
810 // get error message from system
811 LPVOID lpMsgBuf;
812 if ( ::FormatMessage
813 (
814 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
815 NULL,
816 nErrCode,
817 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
818 (LPTSTR)&lpMsgBuf,
819 0,
820 NULL
821 ) == 0 )
822 {
823 // if this happens, something is seriously wrong, so don't use _() here
824 // for safety
825 wxSprintf(s_szBuf, wxS("unknown error %lx"), nErrCode);
826 return s_szBuf;
827 }
828
829
830 // copy it to our buffer and free memory
831 // Crashes on SmartPhone (FIXME)
832 #if !defined(__SMARTPHONE__) /* of WinCE */
833 if( lpMsgBuf != 0 )
834 {
835 wxStrlcpy(s_szBuf, (const wxChar *)lpMsgBuf, WXSIZEOF(s_szBuf));
836
837 LocalFree(lpMsgBuf);
838
839 // returned string is capitalized and ended with '\r\n' - bad
840 s_szBuf[0] = (wxChar)wxTolower(s_szBuf[0]);
841 size_t len = wxStrlen(s_szBuf);
842 if ( len > 0 ) {
843 // truncate string
844 if ( s_szBuf[len - 2] == wxS('\r') )
845 s_szBuf[len - 2] = wxS('\0');
846 }
847 }
848 else
849 #endif // !__SMARTPHONE__
850 {
851 s_szBuf[0] = wxS('\0');
852 }
853
854 return s_szBuf;
855 #else // !__WXMSW__
856 #if wxUSE_UNICODE
857 static wchar_t s_wzBuf[1024];
858 wxConvCurrent->MB2WC(s_wzBuf, strerror((int)nErrCode),
859 WXSIZEOF(s_wzBuf) - 1);
860 return s_wzBuf;
861 #else
862 return strerror((int)nErrCode);
863 #endif
864 #endif // __WXMSW__/!__WXMSW__
865 }
866
867 #endif // wxUSE_LOG