Filter Library Camera Interface Physics

QAFDebug.cpp

Go to the documentation of this file.
00001 /////////////////////////////////////////////////////////////////////////////////
00002 //
00003 //  QAFDebug critical error log
00004 //
00005 //  Copyright (c) 2000-2004
00006 //  Andrew Schetinin
00007 //
00008 //  This software is provided "as is" without express or implied warranty, 
00009 //  and with no claim as to its suitability for any purpose.
00010 //
00011 //  Permission to use or copy this software for any purpose is hereby granted 
00012 //  without fee, provided the above notices are retained on all copies.
00013 //  Permission to modify the code and to distribute modified code is granted,
00014 //  provided the above notices are retained, and a notice that the code was
00015 //  modified is included with the above copyright notice.
00016 //
00017 //  This software accompanies the article "Code that debugs itself"
00018 //  located at http://www.codeproject.com/debug/qafdebug.asp
00019 //
00020 //  You are welcomed to report bugs, send comments and post code modifications 
00021 //  to aschetinin@hotmail.com 
00022 //
00023 /////////////////////////////////////////////////////////////////////////////////
00024 
00025 ///
00026 /// @file       QAFDebug.cpp "../Src/QAFDebug.cpp"
00027 /// @brief      Implementation of functions for reporting critical errors.
00028 ///         
00029 ///                     This is the implementation of the "QAFDebug.h" macros. 
00030 ///
00031 
00032 #include "stdafx.h"
00033 
00034 #include "QAFDebug.h"
00035 
00036 #include <malloc.h>
00037 #include <shlobj.h>
00038 
00039 // Only if reporting is not disabled
00040 #ifndef QAF_DISABLED
00041 
00042 ////////////////////////////////////////////////////////////////////////////////////
00043 // Defines and constants
00044 ////////////////////////////////////////////////////////////////////////////////////
00045 
00046 /// Size of the shared flags stored in the memory-mapped-file 
00047 const DWORD QDEBUG_SHMEMSIZE = sizeof(DWORD);
00048 
00049 /// Buffer length for HRESULT formatting
00050 const DWORD QAFDEBUG_FMT_HRESULT_LEN = 20;
00051 
00052 /// Format string for HRESULT formatting
00053 const LPCTSTR QAFDEBUG_FMT_HRESULT = _T("FAILED(0x%08X)");
00054 
00055 /// Buffer length for ERROR_SUCCESS formatting
00056 const DWORD QAFDEBUG_FMT_ERROR_SUCCESS_LEN = 30;
00057 
00058 /// Format string for ERROR_SUCCESS formatting
00059 const LPCTSTR QAFDEBUG_FMT_ERROR_SUCCESS = _T("(ERROR_SUCCESS != 0x%08X)");
00060 
00061 // DebugOutString() for saving to file:
00062 //
00063 // C:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(137) : Assertion raised
00064 //         time:        2003-12-07 15:48:07:507 
00065 //         process:     0x00000E24 
00066 //         thread:      0x00000E2C 
00067 //         application: C:\Program Files\Microsoft Office\Office10\WINWORD.EXE <1.4.6.7> 
00068 //         module:      C:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.4.6.7> 
00069 //         last error:  6, The handle is invalid. 
00070 //         expression:  DuplicateHandle( NULL, NULL, NULL, NULL, 0, FALSE, 0 ) 
00071 
00072 // IMPORTANT:
00073 //            I limit here format string output to be safe in the buffer of 2048 characters.
00074 //            Otherwise the buffer may be overrun.
00075 
00076 /// Maximum size of the debug record.
00077 const size_t MAX_BUF_SIZE = 2048;
00078 
00079 /// 3 parameters
00080 /// "filename(line) : error message" 
00081 #define QAFDEBUG_FMT_SPOS _T("%.250s(%d) : %.600s")
00082 
00083 /// End of line + tab
00084 #define QAFDEBUG_FMT_PREFIX _T("\r\n\t")
00085 
00086 /// 7 parameters - formatting date and time
00087 /// "        time:        2003-12-07 15:48:07:507"          
00088 #define QAFDEBUG_FMT_DATE QAFDEBUG_FMT_PREFIX _T("time:        ") _T("%04d-%02d-%02d %02d:%02d:%02d:%03d")
00089 
00090 /// 1 parameter - formatting process info
00091 /// "        process:     0x00000E24" 
00092 #define QAFDEBUG_FMT_PROC QAFDEBUG_FMT_PREFIX _T("process:     ") _T("0x%08X")
00093 
00094 /// 1 parameter - formatting thread info
00095 /// "        thread:      0x00000E2C" 
00096 #define QAFDEBUG_FMT_THRE QAFDEBUG_FMT_PREFIX _T("thread:      ") _T("0x%08X")
00097 
00098 /// 1 parameter - formatting application info
00099 /// "        application: C:\Program Files\Microsoft Office\Office10\WINWORD.EXE <1.4.6.7>" 
00100 #define QAFDEBUG_FMT_APPL QAFDEBUG_FMT_PREFIX _T("application: ") _T("%.250s")
00101 
00102 /// 1 parameter - formatting module info
00103 /// "        module:      C:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.4.6.7>" 
00104 #define QAFDEBUG_FMT_MODU QAFDEBUG_FMT_PREFIX _T("module:      ") _T("%.250s")
00105 
00106 /// 1 parameter - formatting last error info
00107 /// "        last error:  6, The handle is invalid." 
00108 #define QAFDEBUG_FMT_LERR QAFDEBUG_FMT_PREFIX _T("last error:  ") _T("%.250s")
00109 
00110 /// 1 parameter - formatting expression info
00111 /// "        expression:  DuplicateHandle( NULL, NULL, NULL, NULL, 0, FALSE, 0 )" 
00112 #define QAFDEBUG_FMT_EXPR QAFDEBUG_FMT_PREFIX _T("expression:  ") _T("%.250s")
00113 
00114 /// The resulting format strings
00115 /// 16 parameters
00116 #define QAFDEBUG_STD_FORMAT \
00117                         QAFDEBUG_FMT_SPOS \
00118                         QAFDEBUG_FMT_DATE \
00119                         QAFDEBUG_FMT_PROC \
00120                         QAFDEBUG_FMT_THRE \
00121                         QAFDEBUG_FMT_APPL \
00122                         QAFDEBUG_FMT_MODU \
00123                         QAFDEBUG_FMT_LERR \
00124                         QAFDEBUG_FMT_EXPR
00125 
00126 // Fixed error messages for faults in the debug engine
00127 #define QAFDEBUG_ERROR_MUTEX_CREATE       QAFDEBUG_ERROR_PREFIX _T("Mutex is not created\r\n")
00128 #define QAFDEBUG_ERROR_MAP_FILE_CREATE    QAFDEBUG_ERROR_PREFIX _T("Mapped-memory file is not created\r\n")
00129 #define QAFDEBUG_ERROR_LOG_FILE_PATH      QAFDEBUG_ERROR_PREFIX _T("Path to the error log file cannot be retrieved\r\n")
00130 #define QAFDEBUG_ERROR_POINTER_NOT_MAPPED QAFDEBUG_ERROR_PREFIX _T("Memory pointer is not mapped to the Mapped-memory file\r\n")
00131 #define QAFDEBUG_ERROR_NULL               QAFDEBUG_ERROR_PREFIX _T("qafOutputDebugString() got an empty error message.\r\n")
00132 #define QAFDEBUG_ERROR_STD_NULL           QAFDEBUG_ERROR_PREFIX _T("QAFDebug::std pointer is not initialized.\r\n")
00133 #define QAFDEBUG_ERROR_OPEN_LOG_FILE      QAFDEBUG_ERROR_PREFIX _T("Debug log file cannot be opened.\r\n")
00134 #define QAFDEBUG_ERROR_ALLOCATE_BUFFER    QAFDEBUG_ERROR_PREFIX _T("Cannot allocate memory for formatting the error message.\r\n")
00135 #define QAFDEBUG_ERROR_NULL_ARGS          QAFDEBUG_ERROR_PREFIX _T("NULL input parameters for formatting the error message.\r\n")
00136 #define QAFDEBUG_ERROR_FORMAT             QAFDEBUG_ERROR_PREFIX _T("Cannot format the error message.\r\n")
00137 #define QAFDEBUG_ERROR_CLOSE_FILE         QAFDEBUG_ERROR_PREFIX _T("Cannot close the debug log file\r\n")
00138 #define QAFDEBUG_ERROR_BACKUP             QAFDEBUG_ERROR_PREFIX _T("Cannot create a backup copy of the debug log file\r\n")
00139 
00140 ////////////////////////////////////////////////////////////////////////////////////
00141 // Service Functions
00142 ////////////////////////////////////////////////////////////////////////////////////
00143 
00144 #pragma warning( push )
00145 #pragma warning( disable : 4100 )       // (possibly) unreferenced formal parameter
00146 
00147 ///
00148 /// @fn ODS
00149 /// @brief Replacement for OutputDebugString() with checking that it is enabled.
00150 ///
00151 inline void ODS( LPCTSTR szMessage )
00152 {
00153         #ifndef QAF_OUTPUTDEBUGSTRING_DISABLED
00154                 OutputDebugString( szMessage );
00155         #endif
00156 }
00157 
00158 #pragma warning( pop )
00159 
00160 ////////////////////////////////////////////////////////////////////////////////////
00161 // Classes
00162 ////////////////////////////////////////////////////////////////////////////////////
00163 
00164 /// A simple synchronization class, designed to be used as an automatic variable.
00165 /// Once its instance is instanciated, it locks its visibility scope. 
00166 class CSync
00167 {
00168 public:
00169         
00170         /// Flag that the critical section object must be constructed or destroyed
00171         enum CSOP { CS_NOP = 0, CS_CREATE = 1, CS_DESTROY = 2 };
00172         
00173         /// Constructor creates and enters the critical section
00174         CSync( CRITICAL_SECTION * pcs, const CSOP csop = CS_NOP )
00175         {
00176                 m_pcs = pcs;
00177                 m_csop = csop;
00178                 if( CS_CREATE == m_csop )
00179                         InitializeCriticalSection( m_pcs ); 
00180                 EnterCriticalSection( m_pcs ); 
00181         }
00182         
00183         /// Destructor leaves and destroys the critical section
00184         ~CSync() 
00185         {
00186                 LeaveCriticalSection( m_pcs ); 
00187                 if( CS_DESTROY == m_csop )
00188                         DeleteCriticalSection( m_pcs ); 
00189         }
00190 
00191 private:
00192         
00193         /// Critical section is used for synchronization
00194         CRITICAL_SECTION * m_pcs;
00195         
00196         /// Flag that the critical section object must be constructed or destroyed
00197         CSOP m_csop;
00198         
00199 };
00200 
00201 ///
00202 /// @class CQAFDebug
00203 /// @brief Class to generate debug log
00204 ///
00205 class CQAFDebug
00206 {
00207 private:
00208 
00209         #ifndef QAF_UNITTEST_DISABLED
00210         /// This is a named mutex that regulates access to the memory-mapped-file.
00211         HANDLE m_hMutex;
00212 
00213         /// Pointer to the shared memory that contains the shared flags.
00214         LPVOID m_pMem; 
00215 
00216         /// Handle of the shared memory-mapped-file.
00217         HANDLE m_hMapObject;  
00218         #endif
00219 
00220         /// Name of the log file, it is initialized by the constructor
00221         #ifndef QAF_LOGFILE_DISABLED
00222         TCHAR m_szLogFileName[MAX_PATH];
00223         #endif
00224 
00225         /// Critical section is used for synchronization the class methods
00226         CRITICAL_SECTION m_cs;
00227 
00228         /// Application name
00229         TCHAR m_szApplicationName[MAX_PATH];
00230 
00231 protected:
00232         
00233         #ifndef QAF_UNITTEST_DISABLED
00234         /// Set the shared flag in the memory mapped file
00235         bool SetSharedFlag( const DWORD dwFlag ) 
00236         { 
00237                 if( ! tryLock() )
00238                         return false;
00239                 
00240                 memcpy( m_pMem, &dwFlag, sizeof(dwFlag) );
00241                 
00242                 unLock();
00243                 
00244                 return true;
00245         } 
00246         
00247         /// Get the shared flag from the memory mapped file
00248         void CQAFDebug::GetSharedFlag( const LPDWORD pdwFlag ) 
00249         { 
00250                 if( (NULL != pdwFlag) && tryLock() )
00251                 {
00252                         memcpy( pdwFlag, m_pMem, sizeof(*pdwFlag) );
00253                         unLock();
00254                 }
00255         }
00256         
00257         /// Try to lock the memory mapped file.
00258         bool tryLock()
00259         {
00260                 return (WaitForSingleObject( m_hMutex, 50 ) == WAIT_OBJECT_0);
00261         }
00262         
00263         /// Unlock the locked memory mapped file.
00264         void unLock()
00265         {
00266                 ReleaseMutex( m_hMutex );
00267         }
00268         #endif
00269         
00270         /// Generate the file name and directory (check that the file is here).
00271         /// Get the buffer and the buffer length in TCHAR characters including tailing 0x00(00).
00272         /// Returns the length of the written string or 0 if the file name cannot be generated.
00273         /// The file name is constructed from the:
00274         /// 1. Try get the folder path from the environment variable QAFDEBUG_LOG_ENV_VAR
00275         /// 2. Try CSIDL_APPDATA (C:\Documents and Settings\username\Application Data) + QAFDEBUG_LOG_SUBFOLDER
00276         /// 3. Try CSIDL_COMMON_APPDATA (C:\Documents and Settings\All Users\Application Data) + QAFDEBUG_LOG_SUBFOLDER
00277         /// 4. Return 0
00278         /// If the folders are missing on the disk, they are created.
00279         static DWORD qafGetLogFileName( LPTSTR szFilenameBuf, const DWORD dwMaxLen );
00280         
00281         /// Get the shared instance of the log class
00282         static CQAFDebug & instance( void )
00283         {
00284                 // Initialize the static instance (it will be a global variable)
00285                 static CQAFDebug std_err;
00286                 // Return the instance
00287                 return std_err;
00288         }
00289         
00290 public:
00291 
00292         /// Constructor. It does some complex initialization of memory shared files and instance pointers.
00293         CQAFDebug()
00294                 #ifndef QAF_UNITTEST_DISABLED
00295                 : m_hMutex(NULL), m_hMapObject(NULL), m_pMem(NULL)
00296                 #endif
00297         {
00298                 CSync sync( &m_cs, CSync::CS_CREATE );
00299         
00300                 if( 0 == SetLogModule( NULL, m_szApplicationName, MAX_PATH ) )
00301                         m_szApplicationName[0] = 0;
00302 
00303                 #ifndef QAF_LOGFILE_DISABLED
00304                 // Initialize the file name
00305                 if( 0 == qafGetLogFileName( m_szLogFileName, MAX_PATH ) )
00306                         m_szLogFileName[0] = 0;
00307                 #endif
00308 
00309                 #ifndef QAF_UNITTEST_DISABLED
00310                 m_hMutex = CreateMutex( NULL, false, QAFDEBUG_SILENCE_MUTEX );
00311                 if( NULL == m_hMutex )
00312                         ODS( QAFDEBUG_ERROR_MUTEX_CREATE );
00313                 
00314                 m_hMapObject = CreateFileMapping( 
00315                         INVALID_HANDLE_VALUE, // use paging file
00316                         NULL,                 // default security attributes
00317                         PAGE_READWRITE,       // read/write access
00318                         0,                    // size: high 32-bits
00319                         QDEBUG_SHMEMSIZE,     // size: low 32-bits
00320                         QDEBUG_SHMEMFILE );   // name of map object
00321                 if( NULL == m_hMapObject ) 
00322                         ODS( QAFDEBUG_ERROR_MAP_FILE_CREATE ); 
00323                 
00324                 // The first process to attach initializes memory.
00325                 bool fInit = (GetLastError() != ERROR_ALREADY_EXISTS); 
00326                 
00327                 // Get a pointer to the file-mapped shared memory.
00328                 m_pMem = MapViewOfFile( 
00329                         m_hMapObject,     // object to map view of
00330                         FILE_MAP_WRITE, // read/write access
00331                         0,              // high offset:  map from
00332                         0,              // low offset:   beginning
00333                         0 );            // default: map entire file
00334                 if( NULL == m_pMem ) 
00335                         ODS( QAFDEBUG_ERROR_POINTER_NOT_MAPPED ); 
00336                 
00337                 if( fInit )
00338                         SetSharedFlag( TRUE );
00339                 #endif
00340         }
00341         
00342         /// Destructor. It deinitializes the shared flags.
00343         ~CQAFDebug()
00344         {
00345                 CSync sync( &m_cs, CSync::CS_DESTROY );
00346                 
00347                 #ifndef QAF_UNITTEST_DISABLED
00348                 unLock();
00349                 #endif
00350 
00351                 #ifndef QAF_LOGFILE_DISABLED
00352                 m_szLogFileName[0] = 0;
00353                 #endif
00354                 
00355                 #ifndef QAF_UNITTEST_DISABLED
00356                 if( NULL != m_pMem )
00357                 {
00358                         UnmapViewOfFile( m_pMem );
00359                         m_pMem = NULL;
00360                 }
00361                 
00362                 if( NULL != m_hMapObject )
00363                 {
00364                         CloseHandle( m_hMapObject );
00365                         m_hMapObject = NULL;
00366                 }
00367                 
00368                 if( NULL != m_hMutex )
00369                 {
00370                         CloseHandle( m_hMutex );
00371                         m_hMutex = NULL;
00372                 }
00373                 #endif
00374         }
00375         
00376         #ifndef QAF_LOGFILE_DISABLED
00377         /// try open the log file, testing its size and moving it to the same log file if needed.
00378         static HANDLE tryOpenLogFile( void )
00379         {
00380                 CSync sync( &instance().m_cs );
00381                 LPTSTR szFilename = instance().m_szLogFileName;
00382                 bool bAlreadyTried = false;
00383                 HANDLE h = INVALID_HANDLE_VALUE;
00384                 while( true )
00385                 {
00386                         h = CreateFile( szFilename, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL );
00387                         if( INVALID_HANDLE_VALUE == h )
00388                         {
00389                                 ODS( QAFDEBUG_ERROR_OPEN_LOG_FILE );
00390                                 return INVALID_HANDLE_VALUE;
00391                         }
00392                         if( bAlreadyTried || ((QAFDEBUG_LOG_FILE_MAX_SIZE / 2) > GetFileSize( h, NULL )) )
00393                                 break;
00394                         if( ! CloseHandle(h) )
00395                         {
00396                                 ODS( QAFDEBUG_ERROR_CLOSE_FILE );
00397                                 return INVALID_HANDLE_VALUE;
00398                         }
00399                         TCHAR szCopyFilename[MAX_PATH] = { 0 };
00400                         _tcscpy( szCopyFilename, szFilename );
00401                         LPTSTR szPos = _tcsrchr( szCopyFilename, _T('\\') );
00402                         szPos[1] = 0;
00403                         _tcscat( szPos, QAFDEBUG_LOG_OLD_FILE_NAME );
00404                         DeleteFile( szCopyFilename ); // ignore the error if the file does not exist
00405                         if( !MoveFile( szFilename, szCopyFilename ) )
00406                         { // report about the error but not exit - continue using the current file
00407                                 ODS( QAFDEBUG_ERROR_BACKUP );
00408                         }
00409                         bAlreadyTried = true;
00410                 }
00411                 return h;
00412         }
00413         #endif
00414 
00415         /// try to enable the log
00416         static bool tryEnable( void )
00417         {
00418                 #ifdef QAF_UNITTEST_DISABLED
00419                 return true;
00420                 #else
00421                 CSync sync( &instance().m_cs );
00422                 return instance().SetSharedFlag( TRUE );
00423                 #endif
00424         }
00425         
00426         /// try to disable the log
00427         static bool tryDisable( void )
00428         {
00429                 #ifdef QAF_UNITTEST_DISABLED
00430                 return true;
00431                 #else
00432                 CSync sync( &instance().m_cs );
00433                 return instance().SetSharedFlag( FALSE );
00434                 #endif
00435         }
00436 
00437         /// Check if the debug log is enabled in the memory mapped file
00438         static bool isEnabled( void )
00439         {
00440                 #ifdef QAF_UNITTEST_DISABLED
00441                         return true;
00442                 #else
00443                         CSync sync( &instance().m_cs );
00444                         DWORD dwTemp = FALSE;
00445                         instance().GetSharedFlag( &dwTemp );
00446                         return (dwTemp == TRUE);
00447                 #endif
00448         }
00449         
00450         /// Return a pointer to a null-terminated string with the application name and version.
00451         static LPCTSTR GetApplication()
00452         {
00453                 return instance().m_szApplicationName;
00454         }
00455         
00456         /// Get the full path and version number of the given module and record them in 
00457         /// the output buffer.
00458         /// The function returns the number of characters in the output buffer 
00459         /// or 0 in a case of any error.
00460         static DWORD SetLogModule( HMODULE hModule, LPTSTR szOutputBuffer, DWORD dwBufSize )
00461         {
00462                 if( (NULL == szOutputBuffer) || (dwBufSize < 2) )
00463                         return 0;
00464                 szOutputBuffer[0] = 0;
00465                 DWORD dwRet = GetModuleFileName( hModule, szOutputBuffer, dwBufSize - 1 );
00466                 if( (0 == dwRet) || (dwRet > (dwBufSize - 2)) )
00467                         return 0;
00468                 DWORD dwModuleFileNameLen = dwRet;
00469                 DWORD dwReserved = 0;
00470                 dwRet = GetFileVersionInfoSize( szOutputBuffer, &dwReserved );
00471                 if( 0 == dwRet )
00472                         return dwModuleFileNameLen;
00473                 void * pVerInfo = malloc( dwRet );
00474                 if( NULL == pVerInfo )
00475                         return dwModuleFileNameLen;
00476                 memset( pVerInfo, 0, dwRet );
00477                 BOOL bRet = GetFileVersionInfo( szOutputBuffer, dwReserved, dwRet, pVerInfo );
00478                 if( !bRet )
00479                 {
00480                         free( pVerInfo );
00481                         return dwModuleFileNameLen;
00482                 }
00483                 UINT uiSize = 0;
00484                 VS_FIXEDFILEINFO * pFixedVerInfo = NULL;
00485                 bRet = VerQueryValue( pVerInfo, _T("\\"), (LPVOID *)(&pFixedVerInfo), &uiSize );
00486                 if( !bRet || (NULL == pFixedVerInfo) || (sizeof(VS_FIXEDFILEINFO) != uiSize) )
00487                 {
00488                         free( pVerInfo );
00489                         return dwModuleFileNameLen;
00490                 }
00491                 const int MAX_TEMP_BUF = 26;
00492                 TCHAR szBuf[MAX_TEMP_BUF] = { 0 };
00493                 _sntprintf( szBuf, 25, _T(" <%d.%d.%d.%d>"),  
00494                         HIWORD(pFixedVerInfo->dwFileVersionMS), LOWORD(pFixedVerInfo->dwFileVersionMS),
00495                         HIWORD(pFixedVerInfo->dwFileVersionLS), LOWORD(pFixedVerInfo->dwFileVersionLS) );
00496                 szBuf[MAX_TEMP_BUF - 1] = 0;
00497                 free( pVerInfo );
00498                 if( (dwModuleFileNameLen + MAX_TEMP_BUF + 1) < dwBufSize )
00499                 {
00500                         _tcscpy( szOutputBuffer + dwModuleFileNameLen, szBuf );
00501                         dwModuleFileNameLen += _tcslen( szBuf );
00502                 }
00503                 return dwModuleFileNameLen;
00504         }
00505         
00506 };
00507 
00508 ////////////////////////////////////////////////////////////////////////////////////
00509 // Implementation
00510 ////////////////////////////////////////////////////////////////////////////////////
00511 
00512 // This is taken from MSDN, there is no such constant in VC++ 6.0 header files
00513 #if !defined(INVALID_FILE_ATTRIBUTES)
00514         const DWORD INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF;
00515 #endif
00516 
00517 /// Return true if the directory exists.
00518 bool DirExists0( LPCTSTR dirName )
00519 {
00520         DWORD dwAttr = GetFileAttributes( dirName );
00521         
00522         if( dwAttr == INVALID_FILE_ATTRIBUTES )
00523                 return false;
00524         
00525         return ((dwAttr & FILE_ATTRIBUTE_DIRECTORY) != 0);
00526 }
00527 
00528 /// Create the full folder path recursively. 
00529 /// Must get the folder full path without tailing '\'.
00530 bool CreateSubFolders0( LPCTSTR szFolderName )
00531 {
00532         // Prepare variables
00533         DWORD dwLen = _tcslen(szFolderName); 
00534         if( dwLen <= 0 )
00535                 return false;
00536         // Copy the input string with removing the trailing '\'
00537         LPTSTR szTemp = (LPTSTR) malloc( (dwLen + 1) * sizeof(TCHAR) );
00538         if( NULL == szTemp )
00539                 return false;
00540         _tcscpy( szTemp, szFolderName );
00541         if( szTemp[dwLen - 1] == _T('\\') )
00542                 dwLen--;
00543         szTemp[dwLen] = _T('\0');
00544         // If the directory exists, that's it!
00545         bool bRet = DirExists0( szTemp );
00546         // If the directory does not exists, check its parent
00547         if( ! bRet )
00548         {
00549                 LPTSTR szPos = _tcsrchr( szTemp, _T('\\') );
00550                 if( NULL != szPos )
00551                 {
00552                         (*szPos) = _T('\0');
00553                         // Call the function recursively
00554                         if( CreateSubFolders0( szTemp ) )
00555                         {
00556                                 // Now try to create the subfolder and exit
00557                                 (*szPos) = _T('\\');
00558                                 bRet = (TRUE == CreateDirectory( szTemp, NULL ));
00559                         }
00560                 } 
00561         }
00562         free( szTemp );
00563         return bRet;
00564 }
00565 
00566 // Taken from the platform SDK
00567 #if !defined(CSIDL_COMMON_APPDATA)
00568         // All Users\Application Data
00569         #define CSIDL_COMMON_APPDATA 0x0023 
00570 #endif
00571 #if !defined(CSIDL_FLAG_CREATE)
00572         // new for Win2K, to force creation of the folder
00573         #define CSIDL_FLAG_CREATE 0x8000 
00574 #endif
00575 
00576 // This part is excluded for Windows CE
00577 #ifndef _WIN32_WCE
00578 
00579 /// Count of the libraries with the SHGetFolderPath function
00580 const int LIBRARY_COUNT = 2;
00581 
00582 /// The first DLL that is checked for the SHGetFolderPathA(W) function
00583 const LPCTSTR LIBRARY_DLL[LIBRARY_COUNT] = { _T("shell32.dll"), _T("shfolder.dll") };
00584 
00585 /// The name of the function (it differs for ASCII and UNICODE builds) 
00586 #ifdef _UNICODE
00587         const LPCSTR SHGetFolderPathFuncName = "SHGetFolderPathW";
00588 #else
00589         const LPCSTR SHGetFolderPathFuncName = "SHGetFolderPathA";
00590 #endif
00591 
00592 /// The folder search function definition (taken from MSDN)
00593 typedef HRESULT (__stdcall* PSHGetFolderPath)  
00594         ( HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR szPath );
00595 
00596 /// My wrapper function that tries to load first shell32.dll and then shfolder.dll
00597 HRESULT SHGetSpecialFolderPathCustom( HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR szPath ) 
00598 {
00599         HRESULT hr = E_FAIL;
00600         for( int i = 0; i < LIBRARY_COUNT; i++ )
00601         {
00602                 HMODULE hModule = LoadLibrary( LIBRARY_DLL[i] );
00603                 if( NULL != hModule )
00604                 {
00605                         PSHGetFolderPath pFunc = (PSHGetFolderPath) GetProcAddress( hModule, SHGetFolderPathFuncName );
00606                         if( NULL != pFunc )
00607                                 hr = pFunc( hwndOwner, nFolder, hToken, dwFlags, szPath );
00608                         FreeLibrary( hModule );
00609                 }
00610                 if( S_OK == hr ) // I cannot test for SUCCEEDED because it may return S_FALSE on error (look in MSDN)
00611                         break;
00612         }
00613         return hr;
00614 }
00615 
00616 #endif // This part is excluded for Windows CE
00617 
00618 /// This is an enumeration from MSDN for newer versions of SHxxx functions.
00619 /// Renamed to be ported easily between VC 6 and VC 7
00620 typedef enum {
00621         Q_SHGFP_TYPE_CURRENT  = 0,   ///< current value for user, verify it exists
00622         Q_SHGFP_TYPE_DEFAULT  = 1,   ///< default value, may not exist
00623 } Q_SHGFP_TYPE;
00624 
00625 /// Generate the file name and directory (check that the file is here).
00626 /// Get the buffer and the buffer length in TCHAR characters including tailing 0x00(00).
00627 /// Returns the length of the written string or 0 if the file name cannot be generated.
00628 /// The file name is constructed from the:
00629 /// 1. Try get the folder path from the environment variable QAFDEBUG_LOG_ENV_VAR
00630 /// 2. Try CSIDL_APPDATA (C:\Documents and Settings\username\Application Data) + QAFDEBUG_LOG_SUBFOLDER
00631 /// 3. Try CSIDL_COMMON_APPDATA (C:\Documents and Settings\All Users\Application Data) + QAFDEBUG_LOG_SUBFOLDER
00632 /// 4. Return 0
00633 /// If the folders are missing on the disk, they are created.
00634 DWORD CQAFDebug::qafGetLogFileName( LPTSTR szFilenameBuf, const DWORD dwMaxLen )
00635 {
00636         DWORD dwRet = QAFDebug::GetLogDir( szFilenameBuf, dwMaxLen );
00637         if( 0 == dwRet )
00638                 return 0;
00639         // If the directory is created, it is o'key and we can continue with the file name
00640         _tcscat( szFilenameBuf, QAFDEBUG_LOG_FILE_NAME );
00641         return _tcslen(szFilenameBuf);
00642 }
00643 
00644 
00645 /////////////////////////////////////////////////////////////////////////////////////////////////
00646 /// Small class that preserves the last error code
00647 /////////////////////////////////////////////////////////////////////////////////////////////////
00648 class CPreserveLastError
00649 {
00650 public:
00651 
00652         /// Constructor saves the last error code or inherits the error code from a chained 
00653         /// instance of this class.
00654         CPreserveLastError( const LPDWORD pdwLastError ) 
00655         { 
00656                 m_bPreserveLastError = (NULL == pdwLastError);
00657                 if( m_bPreserveLastError )
00658                         m_dwLastError = GetLastError();
00659                 else
00660                         m_dwLastError = (*pdwLastError);
00661         }
00662 
00663         /// Destructor restores the saved last error code if it is a self-enough instance.
00664         ~CPreserveLastError() 
00665         { 
00666                 if( m_bPreserveLastError && (GetLastError() != m_dwLastError) ) 
00667                         SetLastError( m_dwLastError ); 
00668         }
00669 
00670         /// Return the last saved error code.
00671         DWORD GetSavedLastError( void )
00672         {
00673                 return m_dwLastError;
00674         }
00675 
00676         /// Return a pointer to the last saved error code 
00677         operator LPDWORD()
00678         {
00679                 return &m_dwLastError;
00680         }
00681 
00682 private:
00683 
00684         /// Saved last error code
00685         DWORD m_dwLastError;
00686 
00687         /// Flag that indicates that the instance is active.
00688         bool m_bPreserveLastError;
00689 
00690 };
00691 
00692 /////////////////////////////////////////////////////////////////////////////////////////////////
00693 // QAFDebug namespace
00694 /////////////////////////////////////////////////////////////////////////////////////////////////
00695 
00696 /// The current module name - internal variable.
00697 TCHAR szCurrentModuleName[MAX_PATH] = { 0 }; 
00698 
00699 /// Set the current module name (not NULL module name will enable the version logging).
00700 void QAFDebug::SetModule( LPCTSTR szModuleName )
00701 {
00702         szCurrentModuleName[0] = 0;
00703         if( (NULL != szModuleName) && (0 != szModuleName[0]) )
00704         {
00705                 HMODULE hModule = GetModuleHandle( szModuleName );
00706                 if( NULL == hModule )
00707                         return;
00708                 CQAFDebug::SetLogModule( hModule, szCurrentModuleName, MAX_PATH );
00709         }
00710 }
00711 
00712 bool QAFDebug::tryEnable(void)
00713 {
00714         return CQAFDebug::tryEnable();
00715 }
00716 
00717 bool QAFDebug::tryDisable(void)
00718 {
00719         return CQAFDebug::tryDisable();
00720 }
00721 
00722 /// Return true if the character is \n or \r
00723 inline bool isRN( const TCHAR ch )
00724 {
00725         return (_T('\r') == ch) || (_T('\n') == ch);
00726 }
00727 
00728 #pragma warning( push )
00729 #pragma warning( disable : 4189 )       // local variable is initialized but not referenced
00730 
00731 void QAFDebug::OutputDebugStringEx( LPCTSTR szFilename, const int iLine, 
00732         LPCTSTR szExpression, LPCTSTR szErrorMessage, const LPDWORD pdwLastError )
00733 {
00734         // Restore the last error upon exit
00735         CPreserveLastError LastError( pdwLastError );
00736         
00737         // Check if the log is disabled by unit tests, in this case skip reporting
00738         if( !CQAFDebug::isEnabled() )
00739                 return;
00740         
00741         // test input parameters
00742         if( NULL == szFilename )
00743                 szFilename = _T("");
00744         if( NULL == szExpression )
00745                 szExpression = _T("");
00746         if( NULL == szErrorMessage )
00747                 szErrorMessage = _T("");
00748 
00749         // C:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(137) : Assertion raised
00750         //         time:        2003-12-07 15:48:07:507 
00751         //         process:     0x00000E24 
00752         //         thread:      0x00000E2C 
00753         //         application: C:\Program Files\Microsoft Office\Office10\WINWORD.EXE <1.4.6.7> 
00754         //         module:      C:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.4.6.7> 
00755         //         last error:  6, The handle is invalid. 
00756         //         expression:  DuplicateHandle( NULL, NULL, NULL, NULL, 0, FALSE, 0 ) 
00757         
00758         // Prepare parameters for the complete error message
00759         SYSTEMTIME st;
00760         GetLocalTime( &st );
00761         DWORD dwProcess = GetCurrentProcessId(), dwThread = GetCurrentThreadId();
00762         
00763         // Decode the last error if needed
00764         TCHAR szLastError[MAX_PATH] = _T("0"); 
00765         if( 0 != LastError.GetSavedLastError() )
00766         {
00767                 _itot( LastError.GetSavedLastError(), szLastError, 10 );
00768                 LPTSTR szPos = szLastError + _tcslen( szLastError );
00769                 _tcscpy( szPos, _T(", ") );
00770                 szPos += 2;
00771                 DWORD dwRet = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
00772                         NULL, LastError.GetSavedLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
00773                         szPos, MAX_PATH - (szPos - szLastError), NULL );
00774                 szPos[dwRet] = 0;
00775                 while( dwRet-- && _tcschr( _T("\n\r"), szPos[dwRet] ) ) // first compare to 0 and then decrement dwRet!
00776                         szPos[dwRet] = 0;
00777         }
00778         
00779         LPTSTR szFmtStr = QAFDEBUG_STD_FORMAT; // Format string, its length should be enough for decimal values too
00780         size_t nBufSize = _tcslen( szFmtStr ) + _tcslen( szFilename ) + _tcslen( szExpression )
00781                 + _tcslen( szErrorMessage ) + _tcslen( szLastError ) 
00782                 + _tcslen( CQAFDebug::GetApplication() ) + _tcslen( szCurrentModuleName ); // In TCHAR characters!
00783         if( nBufSize > MAX_BUF_SIZE )
00784                 nBufSize = MAX_BUF_SIZE;
00785         LPTSTR szBuffer = (LPTSTR) malloc( nBufSize * sizeof(TCHAR) );
00786         if( NULL == szBuffer ) // if the memory cannot be allocated, the function fails
00787         {
00788                 ODS( QAFDEBUG_ERROR_ALLOCATE_BUFFER );
00789                 return; 
00790         }
00791         
00792         // Format the complete error message 
00793         int iRes = _sntprintf( szBuffer, nBufSize, szFmtStr,
00794                 szFilename, iLine, szErrorMessage,
00795                 st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
00796                 dwProcess, 
00797                 dwThread, 
00798                 CQAFDebug::GetApplication(), 
00799                 szCurrentModuleName,
00800                 szLastError,
00801                 szExpression );
00802         DWORD dw = GetLastError();
00803         if( 0 > iRes )
00804         {
00805                 ODS( QAFDEBUG_ERROR_FORMAT );
00806                 free( szBuffer );
00807                 return;
00808         }
00809 
00810         // Fix redundant \r\n
00811         if( (0 != szBuffer[0]) && (0 != szBuffer[1]) )
00812         {
00813                 for( TCHAR * szPos = szBuffer; 0 != szPos[2]; ++szPos  )
00814                 {
00815                         if( isRN( szPos[0] ) && isRN( szPos[2] ) )
00816                                 szPos[0] = _T(' ');
00817                 }
00818         }
00819 
00820         // Add the last \r\n
00821         DWORD dwBufferSize = _tcslen( szBuffer );
00822         if( (szBuffer[dwBufferSize - 1] != _T('\r')) && (szBuffer[dwBufferSize - 1] != _T('\n')) )
00823         {
00824                 _tcscat( szBuffer, _T("\r\n") );
00825                 dwBufferSize += 2;
00826         }
00827 
00828         // Output the complete error message to the Visual Studio IDE
00829         ODS( szBuffer );
00830         
00831         // If the log file is enabled
00832         #ifndef QAF_LOGFILE_DISABLED
00833 
00834         // Add another \r\n for an additional empty line (that simplifies reading)
00835         _tcscat( szBuffer, _T("\r\n") );
00836         dwBufferSize += 2;
00837 
00838         // If requested, encode the error message to UTF8. This define must be set only in UNICODE builds!
00839         #ifdef QAF_SAVE_UNICODE_AS_UTF8
00840                 DWORD dwUtf8BufSize = (dwBufferSize + 1) * sizeof(TCHAR);
00841                 LPSTR szUTF8 = (LPSTR) malloc( dwUtf8BufSize );
00842                 if( NULL != szUTF8 ) // in case of memory overflow the UNICODE string will be written to file
00843                 {
00844                         int nRet = WideCharToMultiByte( CP_UTF8, 0, szBuffer, dwBufferSize, szUTF8, 
00845                                 dwUtf8BufSize, NULL, NULL );
00846                         if( nRet > 0 ) // in case of any error the UNICODE string will be written to file
00847                         {
00848                                 free( szBuffer );
00849                                 szBuffer = (LPTSTR) szUTF8; // I can do this because this string is used as a plain buffer
00850                                 dwBufferSize = nRet; // count of returned bytes - does not include the trailing sero
00851                                 // despite the fact that in MSDN it is written that this function return value counts 
00852                                 // the trailing zero, actually it even does not terminate the output string with \0.
00853                         }
00854                 }
00855         #else
00856                 dwBufferSize *= sizeof(TCHAR); // I must recalculate here the buffer size because it was in characters 
00857         #endif
00858         
00859         // Output the complete error message to file
00860         // Try to write to the file during 200 msec (5 times with sleeps within)
00861         HANDLE h = INVALID_HANDLE_VALUE;
00862         for( int i = 0; i < 5; i++ )
00863         {
00864                 h = CQAFDebug::tryOpenLogFile();
00865                 #ifdef _DEBUG 
00866                         DWORD dw = GetLastError(); // for my debug purposes
00867                 #endif
00868                 if( h != INVALID_HANDLE_VALUE )
00869                 {
00870                         SetFilePointer( h, 0, NULL, FILE_END ); // I cannot check the return value
00871                         #ifdef _DEBUG 
00872                                 dw = GetLastError(); // for my debug purposes
00873                         #endif
00874                         if( ! WriteFile( h, szBuffer, dwBufferSize, &dwBufferSize, NULL ) ) // and I need it in bytes
00875                                 #ifdef _DEBUG 
00876                                         dw = GetLastError(); // for my debug purposes
00877                                 #else
00878                                         dw = 0; // to disable the compiler warning
00879                                 #endif
00880                         
00881                         CloseHandle( h );
00882 
00883                         break;
00884                 }
00885                 Sleep( 40 );
00886         }
00887 
00888         // If the log file is enabled
00889         #endif
00890 
00891         free( szBuffer );
00892 }
00893 
00894 #pragma warning( pop )
00895 
00896 HRESULT QAFDebug::qafReportComError( HRESULT hrStatus, LPCTSTR szFile, const int iLine )
00897 {
00898         if( SUCCEEDED(hrStatus) )
00899                 return hrStatus;
00900 
00901         CPreserveLastError LastError( NULL );
00902 
00903     if( FACILITY_WINDOWS == HRESULT_FACILITY(hrStatus) )
00904         hrStatus = HRESULT_CODE(hrStatus);
00905         
00906         LPTSTR szErrMsg = NULL;
00907         int iFreeErrMsg = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 
00908                 NULL, hrStatus, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), (LPTSTR)&szErrMsg, 0, NULL );
00909         if( (0 == iFreeErrMsg) || (NULL == szErrMsg) )
00910                 szErrMsg = QAFDEBUG_ERROR_NO_MESSAGE;
00911         
00912         TCHAR szBuf[QAFDEBUG_FMT_HRESULT_LEN];
00913         if( 0 >= wsprintf( szBuf, QAFDEBUG_FMT_HRESULT, hrStatus ) )
00914                 szBuf[0] = 0;
00915         
00916         QAFDebug::OutputDebugStringEx( szFile, iLine, szBuf, szErrMsg, static_cast<LPDWORD>(LastError) );
00917         
00918         if( iFreeErrMsg != 0 )
00919         LocalFree( szErrMsg );
00920 
00921         return hrStatus;
00922 }
00923 
00924 DWORD QAFDebug::ReportWinError( const DWORD dwError, LPCTSTR szFile, const int iLine )
00925 {
00926         if( ERROR_SUCCESS == dwError )
00927                 return dwError;
00928 
00929         CPreserveLastError LastError( NULL );
00930 
00931         LPTSTR szErrMsg = NULL;
00932         int iFreeErrMsg = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 
00933                 NULL, dwError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), (LPTSTR)&szErrMsg, 0, NULL );
00934         if( (0 == iFreeErrMsg) || (NULL == szErrMsg) )
00935                 szErrMsg = QAFDEBUG_ERROR_NO_MESSAGE;
00936         
00937         TCHAR szBuf[QAFDEBUG_FMT_ERROR_SUCCESS_LEN];
00938         if( 0 >= wsprintf( szBuf, QAFDEBUG_FMT_ERROR_SUCCESS, dwError ) )
00939                 szBuf[0] = 0;
00940         
00941         QAFDebug::OutputDebugStringEx( szFile, iLine, szBuf, szErrMsg, static_cast<LPDWORD>(LastError) );
00942         
00943         if( iFreeErrMsg != 0 )
00944         LocalFree( szErrMsg );
00945         
00946         return dwError;
00947 }
00948 
00949 DWORD QAFDebug::GetLogDir( LPTSTR szDirBuf, const DWORD dwMaxLen )
00950 {
00951 
00952 // This part is excluded for Windows CE
00953 #ifndef _WIN32_WCE
00954         
00955         // Test three different folders, one set from outside, second for regular applications 
00956         // and third for service applications
00957         for( int i = 1; i <= 3; i++ )
00958         {
00959                 if( 1 == i ) 
00960                 {
00961                         // 1. Try get the folder path from the environment variable QAFDEBUG_LOG_ENV_VAR 
00962                         DWORD dwRet = GetEnvironmentVariable( QAFDEBUG_LOG_ENV_VAR, szDirBuf, dwMaxLen );
00963                         if( (dwRet <= 0) || (dwRet >= dwMaxLen) )
00964                                 continue; // try the second
00965                 }
00966                 else
00967                 {
00968                         if( 2 == i )
00969                         {
00970                                 // 2. Try CSIDL_APPDATA (C:\Documents and Settings\username\Application Data) + QAFDEBUG_LOG_SUBFOLDER
00971                                 if( S_OK != SHGetSpecialFolderPathCustom( NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, Q_SHGFP_TYPE_CURRENT, szDirBuf ) )
00972                                         continue; // try the third
00973                         }
00974                         else
00975                         {
00976                                 // 3. Try CSIDL_COMMON_APPDATA (C:\Documents and Settings\All Users\Application Data) + QAFDEBUG_LOG_SUBFOLDER
00977                                 if( S_OK != SHGetSpecialFolderPathCustom( NULL, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, NULL, Q_SHGFP_TYPE_CURRENT, szDirBuf ) )
00978                                         return 0; // FAILED!!!
00979                         }
00980                         // Add the relative path 
00981                         DWORD dwSize = _tcslen(szDirBuf); 
00982                         if( dwSize > 0 )
00983                         {
00984                                 if( szDirBuf[dwSize - 1] != _T('\\') )
00985                                         _tcscat( szDirBuf, _T("\\") );
00986                                 _tcscat( szDirBuf, QAFDEBUG_LOG_SUBFOLDER );
00987                         }
00988                 }
00989                 // Test the resulting string 
00990                 DWORD dwSizeBuf = _tcslen(szDirBuf);
00991                 if( 0 == dwSizeBuf )
00992                         continue; // This trial failed
00993 
00994                 // Add final '\'
00995                 if( szDirBuf[dwSizeBuf - 1] != _T('\\') )
00996                         _tcscat( szDirBuf, _T("\\") );
00997                 // Check that the directory exists and create missing subdirectories
00998                 if( ! CreateSubFolders0(szDirBuf) )
00999                         continue; // This trial failed
01000                 // Return the result
01001                 return _tcslen(szDirBuf);
01002         } 
01003 
01004 #else // This part is specific for Windows CE
01005 
01006         // This function always return FALSE, even if successful.
01007         SHGetSpecialFolderPath(NULL, szDirBuf, CSIDL_PERSONAL, FALSE);
01008         // Add the relative path 
01009         DWORD dwSize = _tcslen(szDirBuf); 
01010         if( dwSize > 0 )
01011         {
01012                 if( szDirBuf[dwSize - 1] != _T('\\') )
01013                         _tcscat( szDirBuf, _T("\\") );
01014                 _tcscat( szDirBuf, QAFDEBUG_LOG_SUBFOLDER );
01015         }
01016         // Test the resulting string 
01017         DWORD dwSizeBuf = _tcslen(szDirBuf);
01018         if( 0 == dwSizeBuf )
01019                 return 0;
01020 
01021         // Add final '\'
01022         if( szDirBuf[dwSizeBuf - 1] != _T('\\') )
01023                 _tcscat( szDirBuf, _T("\\") );
01024         // Check that the directory exists and create missing subdirectories
01025         if( ! CreateSubFolders0(szDirBuf) )
01026                 return 0;
01027 
01028         // Return the result
01029         return _tcslen(szDirBuf);
01030 
01031 #endif // This part is specific for Windows CE
01032 
01033         // FAILED !!!
01034         return 0;
01035 }
01036 
01037 ///////////////////////////////////////////////
01038 // END OF FILE
01039 ///////////////////////////////////////////////
01040 #endif
01041 

Camera Filter Library documentation © 2004-2007 by Leiden Probe Microscopy