Figure 4   CEventLog.cpp

/*****************************************************************************/
/*                              SOURCE FILE                                  */
/*****************************************************************************/
/*
       $Archive: $

      $Revision: $
          $Date: $
        $Author: $

    Description:   Implementation of the NT Event Log Wrapper Class
*/
static char OBJECT_ID[] = "$Revision: $ : $Date: $";
/*****************************************************************************/

#include "stdafx.h"
#include "CRegistry.h"
#include "CEventLogRecord.h"
#include "CEventLog.h"


#if defined( _DEBUG )
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif


IMPLEMENT_DYNAMIC( CEventLog, CObject );


#if defined( _DEBUG )
#define new DEBUG_NEW
#endif


/*****************************************************************************/
/*
     FUNCTION NAME:  CEventLog::CEventLog
       DESCRIPTION:  ctor
             INPUT:  none
            OUTPUT:  none
           RETURNS:  inits local vars
*/
CEventLog::CEventLog()
{
   Initialize();
}
/* End of function "CEventLog::CEventLog"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CEventLog::~CEventLog
       DESCRIPTION:  dtor. cleans up and disconnects from the event log 
             INPUT:  none
            OUTPUT:  none
           RETURNS:  none
*/
CEventLog::~CEventLog()
{
   if ( INVALID_HANDLE_VALUE != m_hLog )
      Close();

   Initialize();
}
/* End of function "CEventLog::~CEventLog"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CEventLog::Initialize
       DESCRIPTION:  initializes class members
             INPUT:  none
            OUTPUT:  none
           RETURNS:  void 
*/
void CEventLog::Initialize( void )
{
   m_oComputerName.Empty();
   m_oLogName.Empty();

   m_hLog                = INVALID_HANDLE_VALUE;
   m_dwErrorCode         = 0;
   m_dwBytesRead         = 0;
   m_dwBytesInNextRecord = 0;
}
/* End of function "CEventLog::Initialize"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CEventLog::Close
       DESCRIPTION:  disconnects this from the event log
             INPUT:  none
            OUTPUT:  none
           RETURNS:  TRUE if successful, FALSE otherwise
*/
BOOL CEventLog::Close( void )
{
   BOOL bReturn = ::CloseEventLog( m_hLog );

   if ( FALSE == bReturn )
      m_dwErrorCode = ::GetLastError();
   else
      bReturn = TRUE;

   m_hLog = INVALID_HANDLE_VALUE;

   return( bReturn );
}
/* End of function "CEventLog::Close"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CEventLog::GetRecordCount
       DESCRIPTION:  returns the number of event records
             INPUT:  rdwRecCount - the count will go here
            OUTPUT:  none
           RETURNS:  TRUE if successful, FALSE otherwise
*/
BOOL CEventLog::GetRecordCount( DWORD& rdwRecCount )
{
   rdwRecCount = 0; // set this to a known state in case of API failure

   BOOL bReturn = ::GetNumberOfEventLogRecords( m_hLog, &rdwRecCount );

   if ( FALSE == bReturn )
      m_dwErrorCode = ::GetLastError();
   else
      bReturn = TRUE;

   return( bReturn );
}
/* End of function "CEventLog::GetRecordCount"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CEventLog::GetOldestRecordNumber
       DESCRIPTION:  returns the record number of the oldest record in the event log
             INPUT:  none
            OUTPUT:  none
           RETURNS:  the record id of the oldest record in the log
*/
DWORD CEventLog::GetOldestRecordNumber( void )
{
   DWORD dwReturn = 0;

   if ( FALSE != ::GetOldestEventLogRecord( m_hLog, &dwReturn ) )
      return( dwReturn );

   m_dwErrorCode = ::GetLastError();
   return( 0 );
}
/* End of function "CEventLog::GetOldestRecordNumber"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CEventLog::OpenEventLog
       DESCRIPTION:  opens a connection to the event log
             INPUT:  pchLogName - the log to attach to
                     pchComputerName - the computer whose log will be opened
            OUTPUT:  none
           RETURNS:  TRUE if successful, FALSE otherwise
*/
BOOL CEventLog::OpenEventLog( LPCTSTR pchLogName, LPCTSTR pchComputerName )
{
   m_oLogName = pchLogName;

   // pchComputerName can be NULL
   //
   try
   {
      if ( NULL == pchLogName )
      {
         m_dwErrorCode = ERROR_INVALID_PARAMETER;
         return( FALSE );
      }

      BOOL bReturn = TRUE;

      m_hLog = ::OpenEventLog( pchComputerName, pchLogName );

      if ( NULL == m_hLog )
      {
         m_dwErrorCode  = ::GetLastError();
         m_hLog  = INVALID_HANDLE_VALUE;
         bReturn = FALSE;
      }
      else
      {
         if ( NULL == pchComputerName )
         {
            TCHAR achComputerName[ MAX_PATH ];

            ZeroMemory( achComputerName, sizeof( achComputerName ) );

            DWORD dwSize = sizeof( achComputerName ) / sizeof( *( achComputerName ) );

            if ( FALSE != ::GetComputerName( achComputerName, &dwSize ) )
               m_oComputerName = achComputerName;
            else
               m_oComputerName.Empty();
         }
         else
           m_oComputerName = pchComputerName;
      }
      return( bReturn );
   }
   catch( ... )
   {
      m_dwErrorCode = ERROR_EXCEPTION_IN_SERVICE;
      return( FALSE );
   }
}
/* End of function "CEventLog::OpenEventLog"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CEventLog::ReadRecord
       DESCRIPTION:  reads an event record into a CEventLogRecord object
             INPUT:  dwRecNum - the record id to read
                     roRecOut - the output for this method
                     dwReadMethod - the method to use for the read
            OUTPUT:  roRecOut contents are changed
           RETURNS:  TRUE if successful, FALSE otherwise
*/
BOOL CEventLog::ReadRecord( DWORD dwRecNum, CEventLogRecord& roRecOut, DWORD dwReadMethod )
{
   try
   {
      if ( INVALID_HANDLE_VALUE == m_hLog )
      {
         m_dwErrorCode = ERROR_INVALID_PARAMETER;
         return( FALSE );
      }

      BOOL  bReturn = FALSE;
      BYTE  TempBuf[1];   // an address for a 'throw away record'
      DWORD dwReadSize = 0;

      // First, find out how many bytes we need for this Event Record
      //
      bReturn = ReadRecord( dwRecNum, TempBuf, dwReadSize, dwReadMethod );

      m_dwErrorCode = ::GetLastError();

      if ( (FALSE == bReturn ) && ( ERROR_INSUFFICIENT_BUFFER != m_dwErrorCode ) )
         return( FALSE );

      m_dwErrorCode = 0;

      // m_dwBytesInNextRecord now contains the size our pByteBuffer should be
      // (see ReadRecord for more info....)
      //
      BYTE* pByteBuffer = new BYTE[m_dwBytesInNextRecord];

      ZeroMemory( pByteBuffer, m_dwBytesInNextRecord );

      bReturn = ReadRecord( dwRecNum, pByteBuffer, m_dwBytesInNextRecord, dwReadMethod );
      if ( FALSE == bReturn )
         m_dwErrorCode = ::GetLastError();
      else
         roRecOut.CopyFrom( (EVENTLOGRECORD *) pByteBuffer );

      delete [] pByteBuffer;

      // now get the rest of the event log record information
      //
      if ( 0 != roRecOut.m_dwEventID )
         GetRecordInfo( roRecOut );

      return( bReturn );
   }
   catch( ... )
   {
      m_dwErrorCode = ERROR_EXCEPTION_IN_SERVICE;
      return( FALSE );
   }
}
/* End of function "CEventLog::ReadRecord"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CEventLog::ReadRecord
       DESCRIPTION:  reads an event log into a memory buffer
             INPUT:  dwRecNum - the record number to start at
                     pBuffOut - the area to write the event log record to
                     rdwOutBufSize - size of the output buffer
                     dwReadMethod - the method to use to read the record
            OUTPUT:  none
           RETURNS:  TRUE if successful, FALSE otherwise
*/
BOOL CEventLog::ReadRecord( DWORD dwRecNum, LPVOID pBuffOut, DWORD& rdwOutBufSize, DWORD dwReadMethod )
{
   try
   {
      if ( ( NULL == pBuffOut ) || ( INVALID_HANDLE_VALUE == m_hLog ) )
      {
         m_dwErrorCode = ERROR_INVALID_PARAMETER;
         return( FALSE );
      }

      BOOL bReturn = ::ReadEventLog( m_hLog,
                                     dwReadMethod,
                                     dwRecNum,
                                     pBuffOut,
                                     rdwOutBufSize,
                                     &m_dwBytesRead,
                                     &m_dwBytesInNextRecord );

      if ( FALSE == bReturn )
         m_dwErrorCode = ::GetLastError();
      else
         bReturn = TRUE;

      return( bReturn );
   }
   catch( ... )
   {
      m_dwErrorCode = ERROR_EXCEPTION_IN_SERVICE;
      return( FALSE );
   }
}
/* End of function "CEventLog::ReadRecord"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME: CEventLog::GetRecordInfo
       DESCRIPTION: does all the operations required to provide an "event log"
                    type message string to the event log record
             INPUT: roRecord - reference to a record object
            OUTPUT: contents of roRecord are modified
           RETURNS: TRUE if successful, FALSE otherwise
*/
BOOL CEventLog::GetRecordInfo( CEventLogRecord& roRecord )
{
   char       achExpanded[MAX_PATH];
   CRegistry  oRegistry;
   int        status = 0;
   DWORD      dwExpanded;
   HMODULE    hMod;
   LPVOID     lpMsgBuffer;
   CString    oDllName;
   int        iRet;

   // connect to the registry and then to the subkey indicated by the
   // source field of the record
   //
   if ( TRUE != oRegistry.Connect( HKEY_LOCAL_MACHINE, (const char*)m_oComputerName ) )
   {
      roRecord.m_oCompleteMsg = _T("ERROR: Failed To Connect To Registry.");
      return FALSE;
   }

   CString  oRegPath;
   oRegPath = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
   oRegPath += m_oLogName;
   oRegPath += "\\";
   oRegPath += roRecord.m_oSource;

   if ( TRUE != oRegistry.Open( (const char*)oRegPath, KEY_READ ) )
   {
      roRecord.m_oCompleteMsg = _T("ERROR: Failed to Open Event Log in Registry.");
      return FALSE;
   }

   // if a category is specifed, get the category string from the message file
   //
   if ( 0 != roRecord.m_wEventCategory )
   {
      if ( TRUE != oRegistry.GetValue( "CategoryMessageFile", oDllName ) )
      {
         roRecord.m_oCategory = _T("NULL");
      }
      else
      {
         // Expand environment variable strings in the message DLL path name,
         // in case any are there.
         //
         dwExpanded = ExpandEnvironmentStrings( (const char*)oDllName, 
                                                achExpanded, MAX_PATH );

         // Now we've got the message DLL name, load the DLL.
         //
         hMod = LoadLibraryEx( (const char*)achExpanded, 
                               NULL, 
                               DONT_RESOLVE_DLL_REFERENCES );
         if ( hMod )
         {

            iRet = FormatMessage ( FORMAT_MESSAGE_FROM_HMODULE
                                   | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                                   hMod,
                                   roRecord.m_wEventCategory,
                                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                                   (LPTSTR) &lpMsgBuffer,
                                   0,
                                   NULL );
            FreeLibrary( hMod );
            if ( iRet )
               roRecord.m_oCategory = (char*) lpMsgBuffer;
            else
               roRecord.m_oCategory = _T("UNKNOWN");
         }
         else
            roRecord.m_oCategory = _T("NULL");
      }
   }

   // try to get the complete message for this record by using the format
   // string from the indicated module, and passing the strings embedded in
   // the record as a 'va arg list' for use with the format string
   //
   if ( TRUE != oRegistry.GetValue( "EventMessageFile", oDllName ) )
   {
      roRecord.m_oCompleteMsg = _T("ERROR: Failed to Read Message File Source.");
      return FALSE;
   }

   dwExpanded = ExpandEnvironmentStrings( (const char*)oDllName, achExpanded, MAX_PATH );

   // Now we've got the message DLL name, load the DLL.
   //
   hMod = LoadLibraryEx( achExpanded, NULL, DONT_RESOLVE_DLL_REFERENCES );
   if ( !hMod )
   {
      roRecord.m_oCompleteMsg  = _T(" Can't find or load message DLL. " );
      roRecord.m_oCompleteMsg += _T( "Message DLL must be in path or in current directory." );
      return FALSE;
   }

   // prepare the va arg list by first NULLing out all entries
   // then copy a pointer to each of the strings in the record 
   // into one of the pointers in the arg list
   //
   char*  pvaInsertStrs[128];
   for ( int iClear = 0; iClear < 128; iClear++ )
       pvaInsertStrs[iClear] = NULL;   
   if ( roRecord.m_oStringArray.GetSize() > 0 )
   {
      for ( int iLoop = 0; iLoop < roRecord.m_oStringArray.GetSize(); iLoop++ )
      { 
         CString oTemp = roRecord.m_oStringArray.GetAt(iLoop);
         pvaInsertStrs[iLoop] = (char*)(LPCTSTR)oTemp;
      }
   }

   iRet = FormatMessage ( FORMAT_MESSAGE_FROM_HMODULE
                          | FORMAT_MESSAGE_ARGUMENT_ARRAY 
                          | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                          hMod,
                          roRecord.m_dwEventID,
                          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                          (LPTSTR) &lpMsgBuffer,
                          0,
                          pvaInsertStrs );
   FreeLibrary( hMod );
   if ( 0 == iRet )
   {
      DWORD dwErr = GetLastError();

      FormatMessage ( FORMAT_MESSAGE_FROM_SYSTEM 
                      | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                      NULL,
                      dwErr,
                      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                      (LPTSTR) &lpMsgBuffer,
                      0,
                      NULL );
      
      // if a message string was return. scan it and clear
      // out all \r\n characters that may be present
      //
      if ( strlen( (const char*)lpMsgBuffer ) )
      {
         roRecord.m_oCompleteMsg = (const char*)lpMsgBuffer;
         int iPos = roRecord.m_oCompleteMsg.Find("\r\n");
         while ( (-1) != iPos )
         {
            roRecord.m_oCompleteMsg.SetAt(iPos, ' ' );
            roRecord.m_oCompleteMsg.SetAt(iPos+1, ' ' );
            iPos = roRecord.m_oCompleteMsg.Find("\r\n");
         }
      }
      LocalFree( lpMsgBuffer );
      return FALSE;
   }
   else
   {
      if ( strlen( (const char*)lpMsgBuffer ) )
      {
         roRecord.m_oCompleteMsg = (const char*)lpMsgBuffer;
         int iPos = roRecord.m_oCompleteMsg.Find("\r\n");
         while ( (-1) != iPos )
         {
            roRecord.m_oCompleteMsg.SetAt(iPos, ' ' );
            roRecord.m_oCompleteMsg.SetAt(iPos+1, ' ' );
            iPos = roRecord.m_oCompleteMsg.Find("\r\n");
         }
      }
      LocalFree( lpMsgBuffer );
      return TRUE;
   }
   return TRUE;
}
/* end of function "CEventLog::GetRecordInfo" */
/*****************************************************************************/



#if defined( _DEBUG )
void CEventLog::Dump( CDumpContext& roContext ) const
{
   CObject::Dump( roContext );

   roContext << TEXT( "m_hLog = "                ) << m_hLog                << TEXT( "\n" );
   roContext << TEXT( "m_dwErrorCode = "         ) << m_dwErrorCode         << TEXT( "\n" );
   roContext << TEXT( "m_dwBytesRead = "         ) << m_dwBytesRead         << TEXT( "\n" );
   roContext << TEXT( "m_dwBytesInNextRecord = " ) << m_dwBytesInNextRecord << TEXT( "\n" );
   roContext << TEXT( "m_oComputerName = \""     ) << m_oComputerName       << TEXT( "\"\n" );
   roContext << TEXT( "m_oLogName = \""          ) << m_oLogName            << TEXT( "\"\n" );
}
#endif



/*****************************************************************************/
/* Check-in history */
/*
 *$Log: $
*/
/*****************************************************************************/