windows-nt/Source/XPSP1/NT/printscan/print/spooler/dbglib/dbgmsgp.cxx
2020-09-26 16:20:57 +08:00

872 lines
19 KiB
C++

/*++
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<EDebugLevel>(0)),
m_eBreak(static_cast<EDebugLevel>(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<EDebugLevel>( m_eLevel | kDbgNone );
m_eBreak = static_cast<EDebugLevel>( m_eBreak | kDbgNone );
}
VOID
TDebugMsg::
Enable(
VOID
)
{
m_eLevel = static_cast<EDebugLevel>( m_eLevel & ~kDbgNone );
m_eBreak = static_cast<EDebugLevel>( 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<EDebugLevel>(eLevel & ~kDbgPrivateMask);
m_eBreak = static_cast<EDebugLevel>(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<EDebugLevel>(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<TDebugDevice *>( pNode ) );
}
//
// Release the builtin debug device.
//
while( m_pBuiltinDeviceRoot )
{
TDebugNodeDouble *pNode = m_pBuiltinDeviceRoot;
pNode->Remove( &m_pBuiltinDeviceRoot );
TDebugFactory::Dispose( static_cast<TDebugDevice *>( 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<EDebugLevel>(0);
m_eBreak = static_cast<EDebugLevel>(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<EDebugLevel>(eLevel)))
{
StringTrait StrMessage;
StrMessage.pszNarrow = pszMessage;
eLevel = eLevel & ~kDbgUnicode;
Output(static_cast<EDebugLevel>(eLevel), pszFile, uLine, pszModulePrefix, StrMessage);
if (Break(static_cast<EDebugLevel>(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<EDebugLevel>(eLevel)))
{
StringTrait StrMessage;
StrMessage.pszWide = pszMessage;
eLevel = eLevel | kDbgUnicode;
Output(static_cast<EDebugLevel>(eLevel), pszFile, uLine, pszModulePrefix, StrMessage);
if (Break(static_cast<EDebugLevel>(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<LPBYTE>(const_cast<LPTSTR>(static_cast<LPCTSTR>(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<TDebugDevice *>(Iter.Current())->eGetDebugType() & Globals.DebugDevices)
{
static_cast<TDebugDevice *>(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<TDebugDevice *>(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<LPCTSTR>(strArg0),
static_cast<LPCTSTR>(strArg1),
static_cast<LPCTSTR>(strArg2),
static_cast<LPCTSTR>(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);
}
}