1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/common/log.cpp 
   3 // Purpose:     Assorted wxLogXXX functions, and wxLog (sink for logs) 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  33     #include "wx/arrstr.h" 
  35     #include "wx/string.h" 
  39 #include "wx/apptrait.h" 
  40 #include "wx/datetime.h" 
  42 #include "wx/msgout.h" 
  43 #include "wx/textfile.h" 
  44 #include "wx/thread.h" 
  45 #include "wx/private/threadinfo.h" 
  47 #include "wx/vector.h" 
  49 // other standard headers 
  60 #include "wx/msw/wince/time.h" 
  62 #endif /* ! __WXPALMOS5__ */ 
  64 #if defined(__WINDOWS__) 
  65     #include "wx/msw/private.h" // includes windows.h 
  68 #undef wxLOG_COMPONENT 
  69 const char *wxLOG_COMPONENT 
= ""; 
  73 wxTLS_TYPE(wxThreadSpecificInfo
) wxThreadInfoVar
; 
  78 // contains messages logged by the other threads and waiting to be shown until 
  79 // Flush() is called in the main one 
  80 typedef wxVector
<wxLogRecord
> wxLogRecords
; 
  81 wxLogRecords gs_bufferedLogRecords
; 
  83 // this macro allows to define an object which will be initialized before any 
  84 // other function in this file is called: this is necessary to allow log 
  85 // functions to be used during static initialization (this is not advisable 
  86 // anyhow but we should at least try to not crash) and to also ensure that they 
  87 // are initialized by the time static initialization is done, i.e. before any 
  88 // threads are created hopefully 
  90 // the net effect of all this is that you can use Get##name##CS() function to 
  91 // access the critical function without worrying about it being not initialized 
  93 // see also WX_DEFINE_GLOBAL_CONV2() in src/common/strconv.cpp 
  94 #define WX_DEFINE_LOG_CS(name)                                                \ 
  95     inline wxCriticalSection& Get##name##CS()                                 \ 
  97         static wxCriticalSection s_cs##name;                                  \ 
 101     wxCriticalSection *gs_##name##CSPtr = &Get##name##CS() 
 103 // this critical section is used for buffering the messages from threads other 
 104 // than main, i.e. it protects all accesses to gs_bufferedLogRecords above 
 105 WX_DEFINE_LOG_CS(BackgroundLog
); 
 107 // this one is used for protecting ms_aTraceMasks from concurrent access 
 108 WX_DEFINE_LOG_CS(TraceMask
); 
 110 // and this one is used for gs_componentLevels  
 111 WX_DEFINE_LOG_CS(Levels
); 
 113 } // anonymous namespace 
 115 #endif // wxUSE_THREADS 
 117 // ---------------------------------------------------------------------------- 
 118 // non member functions 
 119 // ---------------------------------------------------------------------------- 
 121 // define this to enable wrapping of log messages 
 122 //#define LOG_PRETTY_WRAP 
 124 #ifdef  LOG_PRETTY_WRAP 
 125   static void wxLogWrap(FILE *f
, const char *pszPrefix
, const char *psz
); 
 128 // ---------------------------------------------------------------------------- 
 130 // ---------------------------------------------------------------------------- 
 135 // this struct is used to store information about the previous log message used 
 136 // by OnLog() to (optionally) avoid logging multiple copies of the same message 
 137 struct PreviousLogInfo
 
 145     // previous message itself 
 151     // other information about it 
 152     wxLogRecordInfo info
; 
 154     // the number of times it was already repeated 
 155     unsigned numRepeated
; 
 158 PreviousLogInfo gs_prevLog
; 
 161 // map containing all components for which log level was explicitly set 
 163 // NB: all accesses to it must be protected by GetLevelsCS() critical section 
 164 wxStringToNumHashMap gs_componentLevels
; 
 166 } // anonymous namespace 
 168 // ============================================================================ 
 170 // ============================================================================ 
 172 // ---------------------------------------------------------------------------- 
 173 // helper global functions 
 174 // ---------------------------------------------------------------------------- 
 176 void wxSafeShowMessage(const wxString
& title
, const wxString
& text
) 
 179     ::MessageBox(NULL
, text
.t_str(), title
.t_str(), MB_OK 
| MB_ICONSTOP
); 
 181     wxFprintf(stderr
, wxS("%s: %s\n"), title
.c_str(), text
.c_str()); 
 186 // ---------------------------------------------------------------------------- 
 187 // wxLog class implementation 
 188 // ---------------------------------------------------------------------------- 
 190 unsigned wxLog::LogLastRepeatIfNeeded() 
 192     const unsigned count 
= gs_prevLog
.numRepeated
; 
 194     if ( gs_prevLog
.numRepeated 
) 
 198         msg
.Printf(wxPLURAL("The previous message repeated once.", 
 199                             "The previous message repeated %lu times.", 
 200                             gs_prevLog
.numRepeated
), 
 201                    gs_prevLog
.numRepeated
); 
 203         msg
.Printf(wxS("The previous message was repeated %lu times."), 
 204                    gs_prevLog
.numRepeated
); 
 206         gs_prevLog
.numRepeated 
= 0; 
 207         gs_prevLog
.msg
.clear(); 
 208         DoLogRecord(gs_prevLog
.level
, msg
, gs_prevLog
.info
); 
 216     // Flush() must be called before destroying the object as otherwise some 
 217     // messages could be lost 
 218     if ( gs_prevLog
.numRepeated 
) 
 220         wxMessageOutputDebug().Printf
 
 222             wxS("Last repeated message (\"%s\", %lu times) wasn't output"), 
 224             gs_prevLog
.numRepeated
 
 229 // ---------------------------------------------------------------------------- 
 230 // wxLog logging functions 
 231 // ---------------------------------------------------------------------------- 
 235 wxLog::OnLog(wxLogLevel level
, const wxString
& msg
, time_t t
) 
 237     wxLogRecordInfo info
; 
 240     info
.threadId 
= wxThread::GetCurrentId(); 
 241 #endif // wxUSE_THREADS 
 243     OnLog(level
, msg
, info
); 
 248 wxLog::OnLog(wxLogLevel level
, 
 250              const wxLogRecordInfo
& info
) 
 252     // fatal errors can't be suppressed nor handled by the custom log target 
 253     // and always terminate the program 
 254     if ( level 
== wxLOG_FatalError 
) 
 256         wxSafeShowMessage(wxS("Fatal Error"), msg
); 
 268     if ( !wxThread::IsMain() ) 
 270         logger 
= wxThreadInfo
.logger
; 
 275                 // buffer the messages until they can be shown from the main 
 277                 wxCriticalSectionLocker 
lock(GetBackgroundLogCS()); 
 279                 gs_bufferedLogRecords
.push_back(wxLogRecord(level
, msg
, info
)); 
 281                 // ensure that our Flush() will be called soon 
 284             //else: we don't have any logger at all, there is no need to log 
 289         //else: we have a thread-specific logger, we can send messages to it 
 293 #endif // wxUSE_THREADS 
 300     logger
->CallDoLogNow(level
, msg
, info
); 
 304 wxLog::CallDoLogNow(wxLogLevel level
, 
 306                     const wxLogRecordInfo
& info
) 
 308     if ( GetRepetitionCounting() ) 
 310         if ( msg 
== gs_prevLog
.msg 
) 
 312             gs_prevLog
.numRepeated
++; 
 314             // nothing else to do, in particular, don't log the 
 319         LogLastRepeatIfNeeded(); 
 321         // reset repetition counter for a new message 
 322         gs_prevLog
.msg 
= msg
; 
 323         gs_prevLog
.level 
= level
; 
 324         gs_prevLog
.info 
= info
; 
 327     // handle extra data which may be passed to us by wxLogXXX() 
 328     wxString prefix
, suffix
; 
 330     if ( info
.GetNumValue(wxLOG_KEY_SYS_ERROR_CODE
, &num
) ) 
 332         long err 
= static_cast<long>(num
); 
 334             err 
= wxSysErrorCode(); 
 336         suffix
.Printf(_(" (error %ld: %s)"), err
, wxSysErrorMsg(err
)); 
 341     if ( level 
== wxLOG_Trace 
&& info
.GetStrValue(wxLOG_KEY_TRACE_MASK
, &str
) ) 
 343         prefix 
= "(" + str 
+ ") "; 
 345 #endif // wxUSE_LOG_TRACE 
 347     DoLogRecord(level
, prefix 
+ msg 
+ suffix
, info
); 
 350 void wxLog::DoLogRecord(wxLogLevel level
, 
 352                              const wxLogRecordInfo
& info
) 
 354 #if WXWIN_COMPATIBILITY_2_8 
 355     // call the old DoLog() to ensure that existing custom log classes still 
 358     // as the user code could have defined it as either taking "const char *" 
 359     // (in ANSI build) or "const wxChar *" (in ANSI/Unicode), we have no choice 
 360     // but to call both of them 
 361     DoLog(level
, (const char*)msg
.mb_str(), info
.timestamp
); 
 362     DoLog(level
, (const wchar_t*)msg
.wc_str(), info
.timestamp
); 
 363 #endif // WXWIN_COMPATIBILITY_2_8 
 366     // TODO: it would be better to extract message formatting in a separate 
 367     //       wxLogFormatter class but for now we hard code formatting here 
 371     // don't time stamp debug messages under MSW as debug viewers usually 
 372     // already have an option to do it 
 374     if ( level 
!= wxLOG_Debug 
&& level 
!= wxLOG_Trace 
) 
 378     // TODO: use the other wxLogRecordInfo fields 
 383             prefix 
+= _("Error: "); 
 387             prefix 
+= _("Warning: "); 
 390         // don't prepend "debug/trace" prefix under MSW as it goes to the debug 
 391         // window anyhow and so can't be confused with something else 
 394             // this prefix (as well as the one below) is intentionally not 
 395             // translated as nobody translates debug messages anyhow 
 405     DoLogTextAtLevel(level
, prefix 
+ msg
); 
 408 void wxLog::DoLogTextAtLevel(wxLogLevel level
, const wxString
& msg
) 
 410     // we know about debug messages (because using wxMessageOutputDebug is the 
 411     // right thing to do in 99% of all cases and also for compatibility) but 
 412     // anything else needs to be handled in the derived class 
 413     if ( level 
== wxLOG_Debug 
|| level 
== wxLOG_Trace 
) 
 415         wxMessageOutputDebug().Output(msg 
+ wxS('\n')); 
 423 void wxLog::DoLogText(const wxString
& WXUNUSED(msg
)) 
 425     // in 2.8-compatible build the derived class might override DoLog() or 
 426     // DoLogString() instead so we can't have this assert there 
 427 #if !WXWIN_COMPATIBILITY_2_8 
 428     wxFAIL_MSG( "must be overridden if it is called" ); 
 429 #endif // WXWIN_COMPATIBILITY_2_8 
 432 #if WXWIN_COMPATIBILITY_2_8 
 434 void wxLog::DoLog(wxLogLevel 
WXUNUSED(level
), const char *szString
, time_t t
) 
 436     DoLogString(szString
, t
); 
 439 void wxLog::DoLog(wxLogLevel 
WXUNUSED(level
), const wchar_t *wzString
, time_t t
) 
 441     DoLogString(wzString
, t
); 
 444 #endif // WXWIN_COMPATIBILITY_2_8 
 446 // ---------------------------------------------------------------------------- 
 447 // wxLog active target management 
 448 // ---------------------------------------------------------------------------- 
 450 wxLog 
*wxLog::GetActiveTarget() 
 453     if ( !wxThread::IsMain() ) 
 455         // check if we have a thread-specific log target 
 456         wxLog 
* const logger 
= wxThreadInfo
.logger
; 
 458         // the code below should be only executed for the main thread as 
 459         // CreateLogTarget() is not meant for auto-creating log targets for 
 460         // worker threads so skip it in any case 
 461         return logger 
? logger 
: ms_pLogger
; 
 463 #endif // wxUSE_THREADS 
 465     if ( ms_bAutoCreate 
&& ms_pLogger 
== NULL 
) { 
 466         // prevent infinite recursion if someone calls wxLogXXX() from 
 467         // wxApp::CreateLogTarget() 
 468         static bool s_bInGetActiveTarget 
= false; 
 469         if ( !s_bInGetActiveTarget 
) { 
 470             s_bInGetActiveTarget 
= true; 
 472             // ask the application to create a log target for us 
 473             if ( wxTheApp 
!= NULL 
) 
 474                 ms_pLogger 
= wxTheApp
->GetTraits()->CreateLogTarget(); 
 476                 ms_pLogger 
= new wxLogStderr
; 
 478             s_bInGetActiveTarget 
= false; 
 480             // do nothing if it fails - what can we do? 
 487 wxLog 
*wxLog::SetActiveTarget(wxLog 
*pLogger
) 
 489     if ( ms_pLogger 
!= NULL 
) { 
 490         // flush the old messages before changing because otherwise they might 
 491         // get lost later if this target is not restored 
 495     wxLog 
*pOldLogger 
= ms_pLogger
; 
 496     ms_pLogger 
= pLogger
; 
 503 wxLog 
*wxLog::SetThreadActiveTarget(wxLog 
*logger
) 
 505     wxASSERT_MSG( !wxThread::IsMain(), "use SetActiveTarget() for main thread" ); 
 507     wxLog 
* const oldLogger 
= wxThreadInfo
.logger
; 
 511     wxThreadInfo
.logger 
= logger
; 
 515 #endif // wxUSE_THREADS 
 517 void wxLog::DontCreateOnDemand() 
 519     ms_bAutoCreate 
= false; 
 521     // this is usually called at the end of the program and we assume that it 
 522     // is *always* called at the end - so we free memory here to avoid false 
 523     // memory leak reports from wxWin  memory tracking code 
 527 void wxLog::DoCreateOnDemand() 
 529     ms_bAutoCreate 
= true; 
 532 // ---------------------------------------------------------------------------- 
 533 // wxLog components levels 
 534 // ---------------------------------------------------------------------------- 
 537 void wxLog::SetComponentLevel(const wxString
& component
, wxLogLevel level
) 
 539     if ( component
.empty() ) 
 545         wxCRIT_SECT_LOCKER(lock
, GetLevelsCS()); 
 547         gs_componentLevels
[component
] = level
; 
 552 wxLogLevel 
wxLog::GetComponentLevel(wxString component
) 
 554     wxCRIT_SECT_LOCKER(lock
, GetLevelsCS()); 
 556     while ( !component
.empty() ) 
 558         wxStringToNumHashMap::const_iterator
 
 559             it 
= gs_componentLevels
.find(component
); 
 560         if ( it 
!= gs_componentLevels
.end() ) 
 561             return static_cast<wxLogLevel
>(it
->second
); 
 563         component 
= component
.BeforeLast('/'); 
 566     return GetLogLevel(); 
 569 // ---------------------------------------------------------------------------- 
 571 // ---------------------------------------------------------------------------- 
 573 void wxLog::AddTraceMask(const wxString
& str
) 
 575     wxCRIT_SECT_LOCKER(lock
, GetTraceMaskCS()); 
 577     ms_aTraceMasks
.push_back(str
); 
 580 void wxLog::RemoveTraceMask(const wxString
& str
) 
 582     wxCRIT_SECT_LOCKER(lock
, GetTraceMaskCS()); 
 584     int index 
= ms_aTraceMasks
.Index(str
); 
 585     if ( index 
!= wxNOT_FOUND 
) 
 586         ms_aTraceMasks
.RemoveAt((size_t)index
); 
 589 void wxLog::ClearTraceMasks() 
 591     wxCRIT_SECT_LOCKER(lock
, GetTraceMaskCS()); 
 593     ms_aTraceMasks
.Clear(); 
 596 /*static*/ bool wxLog::IsAllowedTraceMask(const wxString
& mask
) 
 598     wxCRIT_SECT_LOCKER(lock
, GetTraceMaskCS()); 
 600     for ( wxArrayString::iterator it 
= ms_aTraceMasks
.begin(), 
 601                                   en 
= ms_aTraceMasks
.end(); 
 611 // ---------------------------------------------------------------------------- 
 612 // wxLog miscellaneous other methods 
 613 // ---------------------------------------------------------------------------- 
 615 void wxLog::TimeStamp(wxString 
*str
) 
 618     if ( !ms_timestamp
.empty() ) 
 622         (void)time(&timeNow
); 
 625         wxStrftime(buf
, WXSIZEOF(buf
), 
 626                     ms_timestamp
, wxLocaltime_r(&timeNow
, &tm
)); 
 629         *str 
<< buf 
<< wxS(": "); 
 631 #endif // wxUSE_DATETIME 
 636 void wxLog::FlushThreadMessages() 
 638     // check if we have queued messages from other threads 
 639     wxLogRecords bufferedLogRecords
; 
 642         wxCriticalSectionLocker 
lock(GetBackgroundLogCS()); 
 643         bufferedLogRecords
.swap(gs_bufferedLogRecords
); 
 645         // release the lock now to not keep it while we are logging the 
 646         // messages below, allowing background threads to run 
 649     if ( !bufferedLogRecords
.empty() ) 
 651         for ( wxLogRecords::const_iterator it 
= bufferedLogRecords
.begin(); 
 652               it 
!= bufferedLogRecords
.end(); 
 655             CallDoLogNow(it
->level
, it
->msg
, it
->info
); 
 660 #endif // wxUSE_THREADS 
 664     LogLastRepeatIfNeeded(); 
 668 void wxLog::FlushActive() 
 670     if ( ms_suspendCount 
) 
 673     wxLog 
* const log 
= GetActiveTarget(); 
 677         if ( wxThread::IsMain() ) 
 678             log
->FlushThreadMessages(); 
 679 #endif // wxUSE_THREADS 
 685 // ---------------------------------------------------------------------------- 
 686 // wxLogBuffer implementation 
 687 // ---------------------------------------------------------------------------- 
 689 void wxLogBuffer::Flush() 
 693     if ( !m_str
.empty() ) 
 695         wxMessageOutputBest out
; 
 696         out
.Printf(wxS("%s"), m_str
.c_str()); 
 701 void wxLogBuffer::DoLogTextAtLevel(wxLogLevel level
, const wxString
& msg
) 
 703     // don't put debug messages in the buffer, we don't want to show 
 704     // them to the user in a msg box, log them immediately 
 709             wxLog::DoLogTextAtLevel(level
, msg
); 
 713             m_str 
<< msg 
<< wxS("\n"); 
 717 // ---------------------------------------------------------------------------- 
 718 // wxLogStderr class implementation 
 719 // ---------------------------------------------------------------------------- 
 721 wxLogStderr::wxLogStderr(FILE *fp
) 
 729 void wxLogStderr::DoLogText(const wxString
& msg
) 
 731     wxFputs(msg 
+ '\n', m_fp
); 
 734     // under GUI systems such as Windows or Mac, programs usually don't have 
 735     // stderr at all, so show the messages also somewhere else, typically in 
 736     // the debugger window so that they go at least somewhere instead of being 
 738     if ( m_fp 
== stderr 
) 
 740         wxAppTraits 
*traits 
= wxTheApp 
? wxTheApp
->GetTraits() : NULL
; 
 741         if ( traits 
&& !traits
->HasStderr() ) 
 743             wxMessageOutputDebug().Output(msg 
+ wxS('\n')); 
 748 // ---------------------------------------------------------------------------- 
 749 // wxLogStream implementation 
 750 // ---------------------------------------------------------------------------- 
 752 #if wxUSE_STD_IOSTREAM 
 753 #include "wx/ioswrap.h" 
 754 wxLogStream::wxLogStream(wxSTD ostream 
*ostr
) 
 757         m_ostr 
= &wxSTD cerr
; 
 762 void wxLogStream::DoLogText(const wxString
& msg
) 
 764     (*m_ostr
) << msg 
<< wxSTD endl
; 
 766 #endif // wxUSE_STD_IOSTREAM 
 768 // ---------------------------------------------------------------------------- 
 770 // ---------------------------------------------------------------------------- 
 772 wxLogChain::wxLogChain(wxLog 
*logger
) 
 774     m_bPassMessages 
= true; 
 777     m_logOld 
= wxLog::SetActiveTarget(this); 
 780 wxLogChain::~wxLogChain() 
 784     if ( m_logNew 
!= this ) 
 788 void wxLogChain::SetLog(wxLog 
*logger
) 
 790     if ( m_logNew 
!= this ) 
 796 void wxLogChain::Flush() 
 801     // be careful to avoid infinite recursion 
 802     if ( m_logNew 
&& m_logNew 
!= this ) 
 806 void wxLogChain::DoLogRecord(wxLogLevel level
, 
 808                              const wxLogRecordInfo
& info
) 
 810     // let the previous logger show it 
 811     if ( m_logOld 
&& IsPassingMessages() ) 
 812         m_logOld
->LogRecord(level
, msg
, info
); 
 814     // and also send it to the new one 
 815     if ( m_logNew 
&& m_logNew 
!= this ) 
 816         m_logNew
->LogRecord(level
, msg
, info
); 
 820     // "'this' : used in base member initializer list" - so what? 
 821     #pragma warning(disable:4355) 
 824 // ---------------------------------------------------------------------------- 
 826 // ---------------------------------------------------------------------------- 
 828 wxLogInterposer::wxLogInterposer() 
 833 // ---------------------------------------------------------------------------- 
 834 // wxLogInterposerTemp 
 835 // ---------------------------------------------------------------------------- 
 837 wxLogInterposerTemp::wxLogInterposerTemp() 
 844     #pragma warning(default:4355) 
 847 // ============================================================================ 
 848 // Global functions/variables 
 849 // ============================================================================ 
 851 // ---------------------------------------------------------------------------- 
 853 // ---------------------------------------------------------------------------- 
 855 bool            wxLog::ms_bRepetCounting 
= false; 
 857 wxLog          
*wxLog::ms_pLogger      
= NULL
; 
 858 bool            wxLog::ms_doLog        
= true; 
 859 bool            wxLog::ms_bAutoCreate  
= true; 
 860 bool            wxLog::ms_bVerbose     
= false; 
 862 wxLogLevel      
wxLog::ms_logLevel     
= wxLOG_Max
;  // log everything by default 
 864 size_t          wxLog::ms_suspendCount 
= 0; 
 866 wxString        
wxLog::ms_timestamp(wxS("%X"));  // time only, no date 
 868 #if WXWIN_COMPATIBILITY_2_8 
 869 wxTraceMask     
wxLog::ms_ulTraceMask  
= (wxTraceMask
)0; 
 870 #endif // wxDEBUG_LEVEL 
 872 wxArrayString   
wxLog::ms_aTraceMasks
; 
 874 // ---------------------------------------------------------------------------- 
 875 // stdout error logging helper 
 876 // ---------------------------------------------------------------------------- 
 878 // helper function: wraps the message and justifies it under given position 
 879 // (looks more pretty on the terminal). Also adds newline at the end. 
 881 // TODO this is now disabled until I find a portable way of determining the 
 882 //      terminal window size (ok, I found it but does anybody really cares?) 
 883 #ifdef LOG_PRETTY_WRAP 
 884 static void wxLogWrap(FILE *f
, const char *pszPrefix
, const char *psz
) 
 886     size_t nMax 
= 80; // FIXME 
 887     size_t nStart 
= strlen(pszPrefix
); 
 891     while ( *psz 
!= '\0' ) { 
 892         for ( n 
= nStart
; (n 
< nMax
) && (*psz 
!= '\0'); n
++ ) 
 896         if ( *psz 
!= '\0' ) { 
 898             for ( n 
= 0; n 
< nStart
; n
++ ) 
 901             // as we wrapped, squeeze all white space 
 902             while ( isspace(*psz
) ) 
 909 #endif  //LOG_PRETTY_WRAP 
 911 // ---------------------------------------------------------------------------- 
 912 // error code/error message retrieval functions 
 913 // ---------------------------------------------------------------------------- 
 915 // get error code from syste 
 916 unsigned long wxSysErrorCode() 
 918 #if defined(__WXMSW__) && !defined(__WXMICROWIN__) 
 919     return ::GetLastError(); 
 925 // get error message from system 
 926 const wxChar 
*wxSysErrorMsg(unsigned long nErrCode
) 
 929         nErrCode 
= wxSysErrorCode(); 
 931 #if defined(__WXMSW__) && !defined(__WXMICROWIN__) 
 932     static wxChar s_szBuf
[1024]; 
 934     // get error message from system 
 938             FORMAT_MESSAGE_ALLOCATE_BUFFER 
| FORMAT_MESSAGE_FROM_SYSTEM
, 
 941             MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), 
 947         // if this happens, something is seriously wrong, so don't use _() here 
 949         wxSprintf(s_szBuf
, wxS("unknown error %lx"), nErrCode
); 
 954     // copy it to our buffer and free memory 
 955     // Crashes on SmartPhone (FIXME) 
 956 #if !defined(__SMARTPHONE__) /* of WinCE */ 
 959         wxStrlcpy(s_szBuf
, (const wxChar 
*)lpMsgBuf
, WXSIZEOF(s_szBuf
)); 
 963         // returned string is capitalized and ended with '\r\n' - bad 
 964         s_szBuf
[0] = (wxChar
)wxTolower(s_szBuf
[0]); 
 965         size_t len 
= wxStrlen(s_szBuf
); 
 968             if ( s_szBuf
[len 
- 2] == wxS('\r') ) 
 969                 s_szBuf
[len 
- 2] = wxS('\0'); 
 973 #endif // !__SMARTPHONE__ 
 975         s_szBuf
[0] = wxS('\0'); 
 981         static wchar_t s_wzBuf
[1024]; 
 982         wxConvCurrent
->MB2WC(s_wzBuf
, strerror((int)nErrCode
), 
 983                              WXSIZEOF(s_wzBuf
) - 1); 
 986         return strerror((int)nErrCode
); 
 988 #endif  // __WXMSW__/!__WXMSW__