/*++ Copyright (c) 1998-1999 Microsoft Corporation All rights reserved. Module Name: dbgmsgp.cxx Abstract: Debug Library Author: Steve Kiraly (SteveKi) 10-Dec-1995 Revision History: --*/ #include "precomp.hxx" #pragma hdrstop TDebugMsg:: TDebugMsg( VOID ) : m_eLevel(static_cast(0)), m_eBreak(static_cast(0)), m_pDeviceRoot(NULL), m_pstrPrefix(NULL), m_pBuiltinDeviceRoot(NULL) { } TDebugMsg:: ~TDebugMsg( VOID ) { } BOOL TDebugMsg:: Valid( VOID ) const { return !!m_pBuiltinDeviceRoot; } VOID TDebugMsg:: Disable( VOID ) { m_eLevel = static_cast( m_eLevel | kDbgNone ); m_eBreak = static_cast( m_eBreak | kDbgNone ); } VOID TDebugMsg:: Enable( VOID ) { m_eLevel = static_cast( m_eLevel & ~kDbgNone ); m_eBreak = static_cast( m_eBreak & ~kDbgNone ); } BOOL TDebugMsg:: Type( IN EDebugLevel eLevel ) const { return !(m_eLevel & kDbgNone) && ((m_eLevel & eLevel) || (eLevel & kDbgAlways)); } BOOL TDebugMsg:: Break( IN EDebugLevel eLevel ) const { return m_eBreak & eLevel; } /*++ Routine Name: Initialize Routine Description: Arguments: Return Value: None. --*/ VOID TDebugMsg:: Initialize( IN LPCTSTR pszPrefix, IN UINT uDevice, IN INT eLevel, IN INT eBreak ) { if (!m_pBuiltinDeviceRoot) { BOOL bRetval = FALSE; // // Set the debug message and break level. // m_eLevel = static_cast(eLevel & ~kDbgPrivateMask); m_eBreak = static_cast(eBreak & ~kDbgPrivateMask); // // Set the global device flags. // if (!Globals.DebugDevices) { Globals.DebugDevices = uDevice & (kDbgNull | kDbgDebugger | kDbgFile); } // // Set the character type to current compiled type. // m_eLevel = static_cast(m_eLevel | Globals.CompiledCharType); // // Set the prefix string. // m_pstrPrefix = INTERNAL_NEW TDebugString(pszPrefix ? pszPrefix : kstrPrefix); // // Set the prefix string. // if (m_pstrPrefix && m_pstrPrefix->bValid() && m_pstrPrefix->bCat( _T(":"))) { // // Set the additional format strings. // m_pstrFileInfoFormat = INTERNAL_NEW TDebugString(kstrFileInfoFormat); m_pstrTimeStampFormatShort = INTERNAL_NEW TDebugString(kstrTimeStampFormatShort); m_pstrTimeStampFormatLong = INTERNAL_NEW TDebugString(kstrTimeStampFormatLong); m_pstrThreadIdFormat = INTERNAL_NEW TDebugString(kstrThreadIdFormat); if( m_pstrFileInfoFormat && m_pstrFileInfoFormat->bValid() && m_pstrTimeStampFormatShort && m_pstrTimeStampFormatShort->bValid() && m_pstrTimeStampFormatLong && m_pstrTimeStampFormatLong->bValid() && m_pstrThreadIdFormat && m_pstrThreadIdFormat->bValid() ) { // // Attach the default debug devices. // if(Attach(NULL, kDbgDebugger, NULL, &m_pBuiltinDeviceRoot) && Attach(NULL, kDbgFile, kstrDefaultLogFileName, &m_pBuiltinDeviceRoot) && Attach(NULL, kDbgNull, NULL, &m_pBuiltinDeviceRoot)) { bRetval = TRUE; } else { ErrorText( _T("Error: TDebugMsg::Initialize - A default debug device failed to attach!\n") ); } } else { ErrorText( _T("Error: TDebugMsg::Initialize - format string failed construction!\n") ); } } else { ErrorText( _T("Error: TDebugMsg::Initialize - Debug prefix string failed allocation!\n") ); } // // If we failed then unregister, cleanup. // if (!bRetval) { Destroy(); } } else { ErrorText( _T("Error: TDebugMsg::Initialize already initalized!\n") ); } } /*++ Routine Name: Destroy Routine Description: Destroys the internal state of this class, this function does the same work the destructor would. Arguments: None. Return Value: None. --*/ VOID TDebugMsg:: Destroy( VOID ) { // // Release the debug device. // while( m_pDeviceRoot ) { TDebugNodeDouble *pNode = m_pDeviceRoot; pNode->Remove( &m_pDeviceRoot ); TDebugFactory::Dispose( static_cast( pNode ) ); } // // Release the builtin debug device. // while( m_pBuiltinDeviceRoot ) { TDebugNodeDouble *pNode = m_pBuiltinDeviceRoot; pNode->Remove( &m_pBuiltinDeviceRoot ); TDebugFactory::Dispose( static_cast( pNode ) ); } // // Release the string objects. // INTERNAL_DELETE m_pstrPrefix; INTERNAL_DELETE m_pstrFileInfoFormat; INTERNAL_DELETE m_pstrTimeStampFormatShort; INTERNAL_DELETE m_pstrTimeStampFormatLong; INTERNAL_DELETE m_pstrThreadIdFormat; // // Indicate we are not registered. // m_eLevel = static_cast(0); m_eBreak = static_cast(0); m_pDeviceRoot = NULL; m_pBuiltinDeviceRoot = NULL; m_pstrPrefix = NULL; m_pstrFileInfoFormat = NULL; m_pstrTimeStampFormatShort = NULL; m_pstrTimeStampFormatLong = NULL; m_pstrThreadIdFormat = NULL; } /*++ Routine Name: Attach Routine Description: Attach debug device to list of output devices. Arguments: uDevice - Type of debug device to use. pszConfiguration - Pointer to configuration string. Return Value: TRUE debug device attached, FALSE if error occurred. --*/ BOOL TDebugMsg:: Attach( IN HANDLE *phDevice, IN UINT uDevice, IN LPCTSTR pszConfiguration, IN TDebugNodeDouble **ppDeviceRoot ) { BOOL bRetval = FALSE; // // Get access to the debug factory. // TDebugFactory DebugFactory; // // If we failed to create the debug factory then exit. // if (DebugFactory.bValid()) { // // Create the specified debug device using the factory. // TDebugDevice *pDebugDevice = DebugFactory.Produce(uDevice, pszConfiguration, m_eLevel & kDbgUnicode); // // Check if the debug device was created ok. // if (pDebugDevice) { // // Place this device on the debug device list. // if (ppDeviceRoot) { pDebugDevice->Insert(ppDeviceRoot); } else { pDebugDevice->Insert(&m_pDeviceRoot); } // // Copy back the pointer to the debug device. // if (phDevice) { *phDevice = (HANDLE)pDebugDevice; } // // Successfully attached debug device. // bRetval = TRUE; } else { ErrorText( _T("Error: TDebugMsg::bAttach - Debug device creation failed!\n") ); } } else { ErrorText( _T("Error: TDebugMsg::bAttach - Debug factory creation failed!\n") ); } return bRetval; } /*++ Routine Name: Detach Routine Description: Detach the debug device from the device stream. Arguments: phDevice - Pointer to debug device handle. Return Value: None. --*/ VOID TDebugMsg:: Detach( IN HANDLE *phDevice ) { // // We silently ignore non initialized devices, or null pointers. // if (phDevice && *phDevice) { // // Get a usable pointer. // TDebugDevice *pDebugDevice = (TDebugDevice *)*phDevice; // // Remove this device from the debug device list. // pDebugDevice->Remove( &m_pDeviceRoot ); // // Dispose of the device. // TDebugFactory::Dispose( pDebugDevice ); // // Mark this device as released. // *phDevice = NULL; } else { ErrorText( _T("Error: TDebugMsg::vDetach - non initialized or null pointer!\n") ); } } /*++ Routine Name: Msg Routine Description: This function is public overloaded function for sending the message to the output devices. Arguments: eLevel - requested message level pszFile - pointer to file name where message was called uLine - line number where message was called pszModulePrefix - message defined module prefix, used as an override pszMessage - pointer to post formated message string Return Value: None. --*/ VOID TDebugMsg:: Msg( IN UINT eLevel, IN LPCTSTR pszFile, IN UINT uLine, IN LPCTSTR pszModulePrefix, IN LPSTR pszMessage ) const { if (pszMessage) { if (Type(static_cast(eLevel))) { StringTrait StrMessage; StrMessage.pszNarrow = pszMessage; eLevel = eLevel & ~kDbgUnicode; Output(static_cast(eLevel), pszFile, uLine, pszModulePrefix, StrMessage); if (Break(static_cast(eLevel))) { DebugBreak(); } } INTERNAL_DELETE [] pszMessage; } } /*++ Routine Name: Msg Routine Description: This function is public overloaded function for sending the message to the output devices. Arguments: eLevel - requested message level pszFile - pointer to file name where message was called uLine - line number where message was called pszModulePrefix - message defined module prefix, used as an override pszMessage - pointer to post formated message string Return Value: None. --*/ VOID TDebugMsg:: Msg( IN UINT eLevel, IN LPCTSTR pszFile, IN UINT uLine, IN LPCTSTR pszModulePrefix, IN LPWSTR pszMessage ) const { if (pszMessage) { if (Type(static_cast(eLevel))) { StringTrait StrMessage; StrMessage.pszWide = pszMessage; eLevel = eLevel | kDbgUnicode; Output(static_cast(eLevel), pszFile, uLine, pszModulePrefix, StrMessage); if (Break(static_cast(eLevel))) { DebugBreak(); } } INTERNAL_DELETE [] pszMessage; } } /******************************************************************** Private member functions. ********************************************************************/ /*++ Routine Name: Output Routine Description: Outputs the messages to the list of registred debug devices. Arguments: eLevel - requested message level pszFile - pointer to file name where message was called uLine - line number where message was called pszModulePrefix - message defined module prefix, used as an override pszMessage - pointer to post formated message string Return Value: None. --*/ VOID TDebugMsg:: Output( IN EDebugLevel eLevel, IN LPCTSTR pszFileName, IN UINT uLine, IN LPCTSTR pszModulePrefix, IN StringTrait &strMsg ) const { TDebugString strFinal; // // Build the final output string. // if (BuildFinalString(strFinal, eLevel, pszModulePrefix, pszFileName, uLine, strMsg)) { // // Calculate the byte count (less the null terminator) of the final string. // UINT uByteCount = (m_eLevel & kDbgUnicode) ? strFinal.uLen() * sizeof(WCHAR) : strFinal.uLen() * sizeof(CHAR); LPBYTE pByte = reinterpret_cast(const_cast(static_cast(strFinal))); { // // Create interator on built in device list. // TDebugNodeDouble::Iterator Iter(m_pBuiltinDeviceRoot); // // Output this string to all the built in debug devices. // for( Iter.First(); !Iter.IsDone(); Iter.Next() ) { if (static_cast(Iter.Current())->eGetDebugType() & Globals.DebugDevices) { static_cast(Iter.Current())->bOutput(uByteCount, pByte); } } } { // // Create interator on device list. // TDebugNodeDouble::Iterator Iter(m_pDeviceRoot); // // Output this string to all the registered debug devices. // for( Iter.First(); !Iter.IsDone(); Iter.Next() ) { static_cast(Iter.Current())->bOutput( uByteCount, pByte); } } } else { ErrorText(_T("Error: TDebugMsg::vOutput - failed to build format string.\n")); } } /*++ Routine Name: BuildFinalString Routine Description: This routing build the actual string that will be sent to the debug output devices. Arguments: strFinal - string refrence where to return the finale output string. eLevel - debug message level. pszModulePrefix - per message prefix string, can be null. pszFileName - file name were the message was requested. uLine - line number were message was requested. StrMsg - post formated message string. Return Value: TRUE final string was build successfully, FALSE error occurred. --*/ BOOL TDebugMsg:: BuildFinalString( IN TDebugString &strFinal, IN EDebugLevel eLevel, IN LPCTSTR pszModulePrefix, IN LPCTSTR pszFileName, IN UINT uLine, IN StringTrait &StrMsg ) const { LPCTSTR pszFormat; TDebugString strArg0; TDebugString strArg1; TDebugString strArg2; TDebugString strArg3; UINT eFlags = m_eLevel | eLevel; if (!(eFlags & kDbgNoPrefix)) { (VOID)strArg0.bUpdate(pszModulePrefix ? pszModulePrefix : *m_pstrPrefix); } if (!(eFlags & kDbgNoFileInfo)) { (VOID)GetParameter(eFlags & (kDbgFileInfo | kDbgFileInfoLong), strArg1, pszFileName, uLine); } (VOID)GetParameter(eFlags & (kDbgFileInfo | kDbgFileInfoLong), strArg1, pszFileName, uLine); (VOID)GetParameter(eFlags & (kDbgTimeStamp | kDbgTimeStampLong), strArg2, NULL, 0); (VOID)GetParameter(eFlags & kDbgThreadId, strArg3, NULL, 0); if ((eLevel & kDbgUnicode) == (m_eLevel & kDbgUnicode)) { pszFormat = _T("%s%s%s%s %s"); } else { pszFormat = _T("%s%s%s%s %S"); } (VOID)strFinal.bFormat( pszFormat, static_cast(strArg0), static_cast(strArg1), static_cast(strArg2), static_cast(strArg3), StrMsg.pszByte); return strFinal.bValid(); } /*++ Routine Name: GetParameter Routine Description: This function get the parameter for the additinal information displayed in a format string. The flags passed to the message class and to the message function are used a guide. Arguments: eFlags - Flags indicating what parameter to get. strString - place were to return resultant string. pszFileName - pointer to file name to format if requested. uLine - line number for file name format. Return Value: TRUE parmeter was returned in strString, FALSE error. --*/ BOOL TDebugMsg:: GetParameter( IN UINT eFlags, IN OUT TDebugString &strString, IN LPCTSTR pszFileName, IN UINT uLine ) const { BOOL bRetval = TRUE; if (eFlags & kDbgFileInfo) { bRetval = strString.bFormat(*m_pstrFileInfoFormat, StripPathFromFileName(pszFileName), uLine); } if (eFlags & kDbgFileInfoLong) { bRetval = strString.bFormat(*m_pstrFileInfoFormat, pszFileName, uLine); } if (eFlags & kDbgTimeStamp) { bRetval = strString.bFormat(*m_pstrTimeStampFormatShort, GetTickCount()); } if (eFlags & kDbgTimeStampLong) { TCHAR szBuffer[MAX_PATH]; SYSTEMTIME Time; GetSystemTime(&Time); bRetval = SystemTimeToTzSpecificLocalTime(NULL, &Time, &Time) && GetTimeFormat(LOCALE_USER_DEFAULT, 0, &Time, NULL, szBuffer, COUNTOF(szBuffer)) && strString.bFormat(*m_pstrTimeStampFormatLong, szBuffer); } if (eFlags & kDbgThreadId) { bRetval = strString.bFormat(*m_pstrThreadIdFormat, GetCurrentThreadId()); } return bRetval; } /*++ Routine Name: SetMessageFieldFormat Routine Description: This routing allows individual parts of the format string to have a custom format string specifier. Arguments: Field - specified which format field to change. pszFormat - new format string, the caller must know the correct type. Return Value: None. --*/ VOID TDebugMsg:: SetMessageFieldFormat( IN UINT eField, IN LPTSTR pszFormat ) { // // Set the new format string. // if (eField & kDbgFileInfo) { m_pstrFileInfoFormat->bUpdate(pszFormat); } if (eField & kDbgFileInfoLong) { m_pstrFileInfoFormat->bUpdate(pszFormat); } if (eField & kDbgTimeStamp) { m_pstrTimeStampFormatShort->bUpdate(pszFormat); } if (eField & kDbgTimeStampLong) { m_pstrTimeStampFormatLong->bUpdate(pszFormat); } if (eField & kDbgThreadId) { m_pstrThreadIdFormat->bUpdate(pszFormat); } // // If any of the format strings were cleared then // reset them back to the default value. // if (m_pstrFileInfoFormat->bEmpty()) { m_pstrFileInfoFormat->bUpdate(kstrFileInfoFormat); } if (m_pstrTimeStampFormatShort->bEmpty()) { m_pstrTimeStampFormatShort->bUpdate(kstrTimeStampFormatShort); } if (m_pstrTimeStampFormatLong->bEmpty()) { m_pstrTimeStampFormatLong->bUpdate(kstrTimeStampFormatLong); } if (m_pstrThreadIdFormat->bEmpty()) { m_pstrThreadIdFormat->bUpdate(kstrThreadIdFormat); } }