Figure 1   CWebEventProbe.h


#if !defined ( _WEBEVENTPROBE_H_ )
#define _WEBEVENTPROBE_H_

#include "resource.h"


class CWebEventProbe : public CHttpServer
{
private:
    BOOL    m_bHaveIPAddress;
    CString m_oIPAddress;

public:
    CWebEventProbe();
    ~CWebEventProbe();

// Overrides
    //{{AFX_VIRTUAL(CWebEventProbe)
    public:
    virtual BOOL GetExtensionVersion(HSE_VERSION_INFO* pVer);
    //}}AFX_VIRTUAL

    DWORD HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB );
    void  SendEventLog ( CHttpServerContext* pCtxt, 
                         LPCTSTR pchHostName, 
                         LPCTSTR pchEventType, 
                         LPCTSTR pchRecsInPage );

    void GetNextPage( CHttpServerContext* pCtxt,
                      LPCTSTR pchHostName,
                      LPCTSTR pchEventType,
                      LPCTSTR pchFirstRec,
                      LPCTSTR pchRecsInPage );

    void GetRecords( CHttpServerContext* pCtxt,
                     LPCTSTR pchHostName,
                     LPCTSTR pchEventType,
                     DWORD   dwStartAt,
                     DWORD   dwFetchSize );

    void OutputHiddenVariable( CHttpServerContext* pCtxt, 
                               const char* pchVarName,
                               const char* pchValue );

    void OutputHiddenVariable( CHttpServerContext* pCtxt,
                               const char* pchVarName,
                               DWORD       dwValue );

    void OutputTableHeader( CHttpServerContext* pCtxt, 
                            const char* pchCaption, 
                            const char* pchEventType );

    void OpenTable( CHttpServerContext* pCtxt, 
                    const char* pchCaption, 
                    const char* pchEventType );

    void OutputEventRow( CHttpServerContext* pCtxt,
                         CEventLogRecord& roEventRecord );

    void CloseTable( CHttpServerContext* pCtxt );

    BOOL GetHostAddress( CString& roString );

    DECLARE_PARSE_MAP()

    //{{AFX_MSG(CWebEventProbe)
    //}}AFX_MSG
};


#endif


Figure 2   CWebEventProbe.cpp



#define WIN32_LEAN_AND_MEAN 1

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


CWinApp        theApp;
CWebEventProbe theExtension;



BEGIN_PARSE_MAP(CWebEventProbe, CHttpServer)
    ON_PARSE_COMMAND(SendEventLog, CWebEventProbe,ITS_PSTR ITS_PSTR ITS_PSTR )
    ON_PARSE_COMMAND_PARAMS("HostName EventType NumRecsInPage")
    ON_PARSE_COMMAND( GetNextPage, CWebEventProbe, 
                      ITS_PSTR ITS_PSTR ITS_PSTR ITS_PSTR )
    ON_PARSE_COMMAND_PARAMS("SourceServer EventLogType FirstPageRec NumRecsInPage")
END_PARSE_MAP( CWebEventProbe )



/*****************************************************************************/
/*
     FUNCTION NAME:  CWebEventProbe::CWebEventProbe
       DESCRIPTION:  ctor
             INPUT:  none
            OUTPUT:  none
           RETURNS:  none 
*/
CWebEventProbe::CWebEventProbe()
{
    m_bHaveIPAddress = GetHostAddress( m_oIPAddress );
}
/* End of function "CWebEventProbe::CWebEventProbe"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CWebEventProbe::~CWebEventProbe
       DESCRIPTION:  dtor
             INPUT:  none
            OUTPUT:  none
           RETURNS:  none 
*/
CWebEventProbe::~CWebEventProbe()
{
}
/* End of function "CWebEventProbe::~CWebEventProbe"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CWebEventProbe::GetHostAddress
       DESCRIPTION:  Gets the IP address for the host of this dll instance
             INPUT:  roString - reference to a CString for output
            OUTPUT:  contents of roString are modified
           RETURNS:  TRUE if the function worked, FALSE otherwise 
*/
BOOL CWebEventProbe::GetHostAddress( CString& roString )
{
    DWORD      MaxNameLength = MAX_COMPUTERNAME_LENGTH + 1;
    char       lpszHostName[MAX_COMPUTERNAME_LENGTH + 1];
    PHOSTENT   phostent;
    IN_ADDR    inaddr;

    roString = _T("localhost");
    if ( ! GetComputerName( (LPTSTR)lpszHostName, (LPDWORD) &MaxNameLength) )
    {
      return FALSE;
    }
        if ((phostent = gethostbyname(lpszHostName)) == NULL)
    {
      return FALSE;
    }

   memcpy( &inaddr, phostent->h_addr,4 );
   roString = inet_ntoa( inaddr );
   
   return TRUE;
}
/* End of function "CWebEventProbe::GetHostAddress"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CWebEventProbe::GetExtensionVersion
       DESCRIPTION:  called by IIS to determine the type of ISAPI dll this is
             INPUT:  pVer - see mfc docs 
            OUTPUT:  none
           RETURNS:  TRUE
*/
BOOL CWebEventProbe::GetExtensionVersion( HSE_VERSION_INFO* pVer )
{
    CHttpServer::GetExtensionVersion( pVer );

    TCHAR sz[ HSE_MAX_EXT_DLL_NAME_LEN+1 ];
    ISAPIVERIFY( ::LoadString( AfxGetResourceHandle(),
                               IDS_SERVER, 
                               sz, 
                               HSE_MAX_EXT_DLL_NAME_LEN ) );

    _tcscpy( pVer->lpszExtensionDesc, sz );
    return TRUE;
}
/* End of function "CWebEventProbe::GetExtensionVersion"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME: SendEventLog
       DESCRIPTION: Returns a specific event log to the caller from the specified host.
             INPUT: pCtxt - see mfc docs
                    pchHostName - the host to connect to get the log from
                    pchEventType - the specific type of event log to request
                    pchRecsInPage - the number of records the user want to pull on  this request
            OUTPUT: none
           RETURNS: void
*/
void CWebEventProbe::SendEventLog( CHttpServerContext* pCtxt,
                                   LPCTSTR pchHostName,
                                   LPCTSTR pchEventType,
                                   LPCTSTR pchRecsInPage )
{
    StartContent( pCtxt );
    WriteTitle( pCtxt );

    if ( !strlen( pchHostName ) )
    {
      *pCtxt << _T( "ERROR: No Host Specified!\r\n" );
      EndContent( pCtxt ); 
      return;
    }

    // if the user chose to clip the page length, then set the loop bound
    // to the users selection. if the caller chose all records, then just
    // let the record count stay where it was
    //
    DWORD  dwNumEvents = 10000;
    if ( !strstr( pchRecsInPage, "all" ) )
      dwNumEvents = atoi( pchRecsInPage );

    GetRecords( pCtxt, pchHostName, pchEventType, 0, dwNumEvents );

    EndContent( pCtxt ); 
}
/* End of function "CWebEventProbe::SendEventLog"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CWebEventProbe::GetNextPage
       DESCRIPTION:  shell function that calls subordinate to get the next page of records
             INPUT:  pCtxt - the connection context
                     pchHostName - the server to get the log from
                     pchEventType - the log type to get
                     pchFirstRec - the first record to request
                     pchRecsInPage - the number of records to fetch
            OUTPUT:  none
           RETURNS:  void
*/
void CWebEventProbe::GetNextPage( CHttpServerContext* pCtxt,
                                  LPCTSTR pchHostName,
                                  LPCTSTR pchEventType,
                                  LPCTSTR pchFirstRec,
                                  LPCTSTR pchRecsInPage )
{
   StartContent( pCtxt );
   WriteTitle( pCtxt );

    DWORD  dwStartAt   = 0;
    DWORD  dwNumEvents = 0;

    dwStartAt = atol( pchFirstRec );
    dwNumEvents = atol( pchRecsInPage );
    GetRecords( pCtxt, pchHostName, pchEventType, dwStartAt + dwNumEvents, dwNumEvents );
    EndContent( pCtxt ); 
}
/* End of function "CWebEventProbe::GetNextPage"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CWebEventProbe::GetRecords
       DESCRIPTION:  does all the actual work required to get pages of records from the event log
             INPUT:  pCtxt - the output context for the client connection
                     pchHostName - the computer name to the get the log from
                     pchEventType - the event log type to return
                     dwStartAt - the staring 'offset' into the log
                     dwFetchSize - the number of records from the base offset
                                   to return
            OUTPUT:  void
           RETURNS:  void 
*/
void CWebEventProbe::GetRecords( CHttpServerContext* pCtxt,
                                 LPCTSTR pchHostName,
                                 LPCTSTR pchEventType,
                                 DWORD   dwStartAt,
                                 DWORD   dwFetchSize )
{
    DWORD           dwLastRecord = 0;
    DWORD           dwIndex      = 0;
    DWORD           dwNumEvents  = 0;

    CEventLog       oEventLog;
    CEventLogRecord oEventRecord;


    *pCtxt << _T( "<BODY BGCOLOR=\"#FFFFFF\">" );

    // connect to the log file on the specified host
    //
    if ( oEventLog.OpenEventLog( pchEventType, pchHostName ) == FALSE )
    {
      *pCtxt << _T( "ERROR: Could Not Connect to Event Log for Host.\r\n" );
      EndContent( pCtxt ); 
      return;
    }

    if ( oEventLog.GetRecordCount( dwNumEvents ) == FALSE )
    {
      *pCtxt << _T( "ERROR: Could Not Determine Event Log Size.\r\n" );
      EndContent( pCtxt ); 
      return;
    }


    dwLastRecord = oEventLog.GetOldestRecordNumber();

    OutputTableHeader( pCtxt, pchHostName, pchEventType );

    // advance to the first record that is greater than the 
    // "start at cursor"
    //
    while ( dwIndex < dwStartAt )
    {
      oEventLog.ReadRecord( dwIndex + 1, 
                            oEventRecord, 
                            EVENTLOG_FORWARDS_READ | EVENTLOG_SEQUENTIAL_READ );
      dwIndex++;
   }

    // now loop over the log file getting records until the count of
    // fetched records matches the users request
    //
    while ( dwIndex < ( dwStartAt + dwFetchSize ) )
    {
      if ( TRUE == oEventLog.ReadRecord( dwIndex + 1, 
                                         oEventRecord, 
                                         EVENTLOG_FORWARDS_READ | 
                                         EVENTLOG_SEQUENTIAL_READ ) )
      {
         OutputEventRow( pCtxt, oEventRecord );
      }
      dwIndex++;
    }
    CloseTable( pCtxt );

    // now if the user did not request the entire log file, 
    // put out some context data with a simple form wrapper
    // in case the user comes back with a 'next page' command
    //
    // bug bug bug. you may need to replace local host here....
    //
    if ( !( dwStartAt == 0 ) || !( dwFetchSize == 10000 ) )
    {
      *pCtxt << _T("<hr><p>");
      *pCtxt << _T("<FORM action=\"http://");
      *pCtxt << m_oIPAddress;
      *pCtxt << _T("/scripts/weasel.dll?GetNextPage\" method=post>");
      *pCtxt << _T("<p>");
      *pCtxt << _T("<INPUT type=\"submit\" value=\"Get Next Page\">");
      OutputHiddenVariable( pCtxt, "EventLogType",  pchEventType );
      OutputHiddenVariable( pCtxt, "SourceServer",  pchHostName );
      OutputHiddenVariable( pCtxt, "FirstPageRec",  dwStartAt );
      OutputHiddenVariable( pCtxt, "NumRecsInPage", dwFetchSize );
      *pCtxt << _T("</FORM>");
   }
}

/* End of function "CWebEventProbe::GetRecords"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CWebEventProbe::OutputHiddenVariable
       DESCRIPTION:  does the work required to output a hidden variable to the  
                     context
             INPUT:  pCtxt - the client context 
                     pchVarName - the name of the variable
                     pchValue - the value for the variable
            OUTPUT:  none
           RETURNS:  void
*/

void CWebEventProbe::OutputHiddenVariable( CHttpServerContext* pCtxt,
                                           const char* pchVarName,
                                           const char* pchValue )
{
   *pCtxt << _T("<INPUT TYPE=HIDDEN NAME=");
   *pCtxt << pchVarName;
   *pCtxt << _T(" VALUE=");
   *pCtxt << pchValue;
   *pCtxt << _T(">\r\n");
}

/* End of function "CWebEventProbe::OutputHiddenVariable"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CWebEventProbe::OutputHiddenVariable
       DESCRIPTION:  does the work required to output a hidden variable to the 
                     context
             INPUT:  pCtxt - the client context 
                     pchVarName - the name of the variable
                     dwValue - the value for the variable
            OUTPUT:  none
           RETURNS:  void
*/

void CWebEventProbe::OutputHiddenVariable( CHttpServerContext* pCtxt,
                                           const char* pchVarName,
                                           DWORD       dwValue )
{
   *pCtxt << _T("<INPUT TYPE=HIDDEN NAME=");
   *pCtxt << pchVarName;
   *pCtxt << _T(" VALUE=");
   *pCtxt << (long)dwValue;
   *pCtxt << _T(">\r\n");
}

/* End of function "CWebEventProbe::OutputHiddenVariable"
/*****************************************************************************/


/*****************************************************************************/
/*
    FUNCTION NAME:  CWebEventProbe::OutputTableHeader
      DESCRIPTION:  calls on a subordinate to start the table, then outputs the 
                    title row.
            INPUT:  pCtxt - the client context 
                    pchCaption - the caption text for the table
                    pchEventType - the type of event log being requested
           OUTPUT:  none
          RETURNS:  void  
*/

void CWebEventProbe::OutputTableHeader( CHttpServerContext* pCtxt, 
                                        const char* pchCaption, 
                                        const char* pchEventType )
{
   OpenTable( pCtxt, pchCaption, pchEventType );
   *pCtxt << _T("<TR><TD NOWRAP ALIGN=CENTER VALIGN=CENTER> Event Type </TD>");
   *pCtxt << _T("<TD ALIGN=CENTER VALIGN=CENTER> Date </TD>");
   *pCtxt << _T("<TD ALIGN=CENTER VALIGN=CENTER> Time </TD>");
   *pCtxt << _T("<TD ALIGN=CENTER VALIGN=CENTER> Event Source </TD>");
   *pCtxt << _T("<TD ALIGN=CENTER VALIGN=CENTER> Category </TD>");
   *pCtxt << _T("<TD NOWRAP ALIGN=CENTER VALIGN=CENTER> Event ID </TD>");
   *pCtxt << _T("<TD ALIGN=CENTER VALIGN=CENTER>Message</TD></TR>");
}

/* End of function "CWebEventProbe::OutputTableHeader"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CWebEventProbe::OpenTable
       DESCRIPTION:  does the work required to start an HTML table. Optionally
                     applies a caption
             INPUT:  pCtxt - the client context
                     pchCaption - the caption text (can be NULL)
                     pchEventType - the type of event log being requested
            OUTPUT:  none
           RETURNS:  void
*/

void CWebEventProbe::OpenTable( CHttpServerContext* pCtxt, 
                                const char* pchCaption, 
                                const char* pchEventType )
{
   *pCtxt << _T("<TABLE BORDER>") << _T("\r\n");

   if ( NULL != pchCaption )
      *pCtxt << _T("<CAPTION ALIGH=TOP> <B><I><FONT SIZE = 5>")
             << pchEventType 
             << _T(" Event Log for ") 
             << pchCaption 
             << _T("</FONT></I></B></CAPTION>") << _T("\r\n");
}

/* End of function "CWebEventProbe::OpenTable"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CWebEventProbe::CloseTable
       DESCRIPTION:  closes out a table
             INPUT:  pCtxt - the output context 
            OUTPUT:  none
           RETURNS:  void
*/

void CWebEventProbe::CloseTable( CHttpServerContext* pCtxt )
{
   *pCtxt << _T("</TABLE>") << _T("\r\n\r\n");
}

/* End of function "CWebEventProbe::CloseTable"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CWebEventProbe::OutputEventRow
       DESCRIPTION:  does the work required to convert an CEventLogRecord to an 
                     HTML output row.
             INPUT:  pCtxt - the output context
                     roEventRecord - the event record to convert/display
            OUTPUT:  none
           RETURNS:  void  
*/

void CWebEventProbe::OutputEventRow( CHttpServerContext* pCtxt,
                                     CEventLogRecord& roEventRecord )
{
   CString oTemp;

   // Event Type Cell
   //
   *pCtxt << _T("<TR><TD>");    
   switch ( roEventRecord.m_wEventType )
   {
      // bug bug bug ... you may need to change the path to this gif.....
      //
      case EVENTLOG_ERROR_TYPE:
         *pCtxt << _T("<IMG SRC=HTTP://");
         *pCtxt << m_oIPAddress;
         *pCtxt << _T("/STOP.GIF ALIGN=MIDDLE ALT=ERROR>");
      break;

      case EVENTLOG_WARNING_TYPE:
         *pCtxt << _T("<IMG SRC=HTTP://");
         *pCtxt << m_oIPAddress;
         *pCtxt << _T("/WARN.GIF ALIGN=MIDDLE ALT=WARNING>");
      break;

      case EVENTLOG_INFORMATION_TYPE:
         *pCtxt << _T("<IMG SRC=HTTP://");
         *pCtxt << m_oIPAddress;
         *pCtxt << _T("/INFO.GIF ALIGN=MIDDLE ALT=INFO>");
      break;

      case EVENTLOG_AUDIT_SUCCESS:
         *pCtxt << _T("<IMG SRC=HTTP://");
         *pCtxt << m_oIPAddress;
         *pCtxt << _T("/INFO.GIF ALIGN=MIDDLE ALT=SUCCESS>");
      break;

      case EVENTLOG_AUDIT_FAILURE:
         *pCtxt << _T("<IMG SRC=HTTP://");
         *pCtxt << m_oIPAddress;
         *pCtxt << _T("/STOP.GIF ALIGN=MIDDLE ALT=FAILURE>");
      break;
   }
   *pCtxt << _T("</TD>\r\n");

   // date part   
   //
   *pCtxt << _T("   <TD>");
   oTemp = roEventRecord.m_oTimeGenerated.Format("%m/%d/%Y");
   *pCtxt << (const char*) oTemp;
   *pCtxt << _T("</TD>\r\n");

   // time part
   //
   *pCtxt << _T("   <TD>");
   oTemp = roEventRecord.m_oTimeGenerated.Format("%I:%M:%S%p");
   *pCtxt << (const char*) oTemp;
   *pCtxt << _T("</TD>\r\n");

   // Event Source
   //
   *pCtxt << _T("   <TD>");
   *pCtxt << (const char*) roEventRecord.m_oSource;
   *pCtxt << _T("</TD>\r\n");

   // Category
   //
   *pCtxt << _T("   <TD>");
   if ( !roEventRecord.m_wEventCategory )
   {
      oTemp.Format("%u", roEventRecord.m_wEventCategory );
      *pCtxt << (const char*) oTemp;
   }
   else
      *pCtxt << (const char*)roEventRecord.m_oCategory;
   *pCtxt << _T("</TD>\r\n");

   // EventID (masked)
   //
   *pCtxt << _T("   <TD>");
   oTemp.Format("%u", roEventRecord.m_dwEventID & 0x0000FFFF );
   *pCtxt << (const char*) oTemp;
   *pCtxt << _T("</TD>\r\n");

   // Event Message
   //
   *pCtxt << _T("   <TD>");
   *pCtxt << (const char*) roEventRecord.m_oCompleteMsg;
   *pCtxt << _T("</TD>") << _T("\r\n\r\n");
}

/* End of function "CWebEventProbe::OutputEventRow"
/*****************************************************************************/


/*****************************************************************************/
/*
     FUNCTION NAME:  CWebEventProbe::HttpExtensionProc
       DESCRIPTION:  the entry point
             INPUT:  pECB -- see mfc docs
            OUTPUT:  none
           RETURNS:  DWORD 
*/

DWORD CWebEventProbe::HttpExtensionProc( EXTENSION_CONTROL_BLOCK* pECB )
{
    return CHttpServer::HttpExtensionProc( pECB );
}

/* End of function "CWebEventProbe::HttpExtensionProc"
/*****************************************************************************/


#if 0
BEGIN_MESSAGE_MAP(CWebEventProbe, CHttpServer)
    //{{AFX_MSG_MAP(CWebEventProbe)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif    // 0


Figure 3   CEventLog.h



#if ! defined( _CEVENTLOG_H_ )
#define _CEVENTLOG_H_

class CEventLogRecord;


class CEventLog : public CObject
{
    DECLARE_DYNAMIC( CEventLog )

public:
    CEventLog();
    virtual ~CEventLog();

    BOOL  Close( void );

    BOOL  GetRecordCount( DWORD& rdRecCount );
    DWORD GetOldestRecordNumber( void );

    BOOL  OpenEventLog( LPCTSTR pchLogName, LPCTSTR pchComputerName = NULL );

    BOOL  ReadRecord( DWORD dwRecNum, 
                     CEventLogRecord& roRecOut, 
                     DWORD dwReadMethod = EVENTLOG_FORWARDS_READ |
                                          EVENTLOG_SEQUENTIAL_READ );
    BOOL  ReadRecord( DWORD  dwRecNum, 
                     LPVOID pBuffOut, 
                     DWORD& rdwOutBufSize, 
                     DWORD  dwReadMethod = EVENTLOG_FORWARDS_READ |
                                           EVENTLOG_SEQUENTIAL_READ );

    BOOL  GetRecordInfo( CEventLogRecord& roRecord );

protected:
    void   Initialize( void );

private:
    CString m_oComputerName;
    CString m_oLogName;
    HANDLE  m_hLog;
    DWORD   m_dwErrorCode;
    DWORD   m_dwBytesRead;
    DWORD   m_dwBytesInNextRecord;

private:
    // don't allow this class to be passed by value
    //
    CEventLog( const CEventLog& ){};
    CEventLog& operator= ( const CEventLog& ) { return( *this ); };

#if defined( _DEBUG )
    virtual void Dump( CDumpContext& roContext ) const;
#endif
};

#endif

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: $
*/
/*****************************************************************************/

Figure 5   CEventLogRecord.h


 #if ! defined ( _EVENTLOGRECORD_H_ )
 #define _EVENTLOGRECORD_H_
 
 class CEventLogRecord : public CObject
 {
 public:
    CEventLogRecord();
    CEventLogRecord( const CEventLogRecord& source );
    CEventLogRecord( const EVENTLOGRECORD * source );
 
    virtual ~CEventLogRecord();
 
    // Data
    // NOTE: these are kept public for quick and easy access
    //
    CString      m_oSource;
    CString      m_oCategory;
    CString      m_oComputerName;
    DWORD        m_dwRecNum;
    DWORD        m_dwReserved;
    CTime        m_oTimeGenerated;
    CTime        m_oTimeWritten;
    DWORD        m_dwEventID;
    WORD         m_wEventType;
    WORD         m_wEventCategory;
    WORD         m_wPairedEventFlags;
    DWORD        m_dwClosingRecNum;
    CByteArray   m_oUserSID;
    CByteArray   m_oData;
    CStringArray m_oStringArray;
    CString      m_oCompleteMsg;
 
    // Methods
    //
    void CopyFrom( const EVENTLOGRECORD* pxSource );
    void CopyFrom( const CEventLogRecord& roSource );
    void Initialize( void );
 
 #if defined( _DEBUG )
    virtual void Dump( CDumpContext& roContext ) const;
 #endif
 };   
 
 
#endif

Figure 6   CEventLogRecord.cpp


 #include "stdafx.h"
 #include "CEventLogRecord.h"
 
 
 #if defined( _DEBUG )
 #undef THIS_FILE
 static char BASED_CODE THIS_FILE[] = __FILE__;
 #endif
 
 
 #if defined( _DEBUG )
 #define new DEBUG_NEW
 #endif
 
 
 CEventLogRecord::CEventLogRecord()
 {
     Initialize();
 }
 
 CEventLogRecord::CEventLogRecord( const CEventLogRecord& roSource )
 {
     Initialize();
     CopyFrom( roSource );
 }
 
 CEventLogRecord::CEventLogRecord( const EVENTLOGRECORD* pxSource )
 {
     Initialize();
     CopyFrom( pxSource );
 }
 
 CEventLogRecord::~CEventLogRecord()
 {
     Initialize();
 }
 
 /*****************************************************************************/
 
 void CEventLogRecord::CopyFrom( const CEventLogRecord& roSource )
 {
     // don't copy self
    if ( &roSource == this )
       return;
 
     m_oSource           = roSource.m_oSource;
     m_oComputerName     = roSource.m_oComputerName;
     m_dwRecNum          = roSource.m_dwRecNum;
     m_dwReserved        = roSource.m_dwReserved;
     m_oTimeGenerated    = roSource.m_oTimeGenerated;
     m_oTimeWritten      = roSource.m_oTimeWritten;
     m_dwEventID         = roSource.m_dwEventID;
     m_wEventType        = roSource.m_wEventType;
     m_wEventCategory    = roSource.m_wEventCategory;
     m_wPairedEventFlags = roSource.m_wPairedEventFlags;
     m_dwClosingRecNum   = roSource.m_dwClosingRecNum;
 
     m_oUserSID.Copy( roSource.m_oUserSID );
     m_oData.Copy( roSource.m_oData );
     m_oStringArray.Copy( roSource.m_oStringArray );
 }
 
 /*****************************************************************************/
 
 void CEventLogRecord::CopyFrom( const EVENTLOGRECORD* pxSource )
 {
     Initialize();
 
     if ( NULL == pxSource )
       return;
 
     try
     {
       m_dwRecNum           = pxSource->RecordNumber;
       m_dwReserved         = pxSource->Reserved;
       m_oTimeGenerated     = CTime( pxSource->TimeGenerated );
       m_oTimeWritten       = CTime( pxSource->TimeWritten   );
       m_dwEventID          = pxSource->EventID;
       m_wEventType         = pxSource->EventType;
       m_wEventCategory     = pxSource->EventCategory;
       m_wPairedEventFlags  = pxSource->ReservedFlags;
       m_dwClosingRecNum    = pxSource->ClosingRecordNumber;
 
       // Here's where things start to get a little interesting...
 
       BYTE* pByte    = (BYTE*) pxSource;
       BYTE  AddByte  = 0;
       DWORD dwCurPos = pxSource->UserSidOffset;
       DWORD dwIndex  = 0;
 
       while( dwIndex < pxSource->UserSidLength )
       {
          AddByte = pByte[ dwCurPos ];
          m_oUserSID.Add( AddByte );
          dwCurPos++;
          dwIndex++;
       }
 
       dwIndex = 0;
       dwCurPos = pxSource->DataOffset;
 
       while( dwIndex < pxSource->DataLength )
       {
          m_oData.Add( pByte[ dwCurPos ] );
          dwCurPos++;
          dwIndex++;
       }
 
       dwCurPos = sizeof( EVENTLOGRECORD );
 
       TCHAR chTemp = 0;
 
       // Next comes the SourceName
 
       while( pByte[ dwCurPos ] != 0 )
       {
          chTemp = pByte[ dwCurPos ];
          m_oSource += chTemp;
          dwCurPos++;
       }
 
       while( pByte[ dwCurPos ] == 0 )
       {
          dwCurPos++;
       }
 
       // ComputerName
 
       while( pByte[ dwCurPos ] != 0 )
       {
          m_oComputerName += (TCHAR) pByte[ dwCurPos ];
          dwCurPos++;
       }
 
       // Strings
 
       CString oStrTemp( _T( "" ) );
       int iNumOfStrings = 0;
 
       dwCurPos = pxSource->StringOffset;
 
       while( iNumOfStrings < pxSource->NumStrings )
       {
          oStrTemp.Empty();
 
          while( pByte[ dwCurPos ] != 0 )
          {
             chTemp = pByte[ dwCurPos ];
             oStrTemp += chTemp;
             dwCurPos++;
          }
 
          m_oStringArray.Add( oStrTemp );
 
          iNumOfStrings++;
 
          // Advance to the next string
 
          if ( pByte[ dwCurPos ] == 0 && iNumOfStrings < pxSource->NumStrings )
          {
             while( pByte[ dwCurPos ] == 0 )
             {
                dwCurPos++;
             }
          }
       }
     }
     catch( ... )
     {
       Initialize();
     }
 }
 
 /*****************************************************************************/
 
 void CEventLogRecord::Initialize( void )
 {
     m_oSource.Empty();
     m_oComputerName.Empty();
 
     m_dwRecNum          = 0;
     m_dwReserved        = 0;
     m_oTimeGenerated    = CTime( 0 );
     m_oTimeWritten      = CTime( 0 );
     m_dwEventID         = 0;
     m_wEventType        = 0;
     m_wEventCategory    = 0;
     m_wPairedEventFlags = 0;
     m_dwClosingRecNum   = 0;
 
     m_oUserSID.RemoveAll();
     m_oData.RemoveAll();
     m_oStringArray.RemoveAll();
 }
 
 /*****************************************************************************/
 
 #if defined( _DEBUG )
 void CEventLogRecord::Dump( CDumpContext& roContext ) const
 {
     CObject::Dump( roContext );
 
     roContext << _T( "LogRecord:\n" );
     roContext << _T( "{\n" );
     roContext << _T( "   m_oSource       = " ) << m_oSource           << _T( "\n" );
     roContext << _T( "   m_oComputerName = " ) << m_oComputerName     << _T( "\n" );
     roContext << _T( "   m_dwRecNum      = " ) << m_dwRecNum          << _T( "\n" );
     roContext << _T( "   m_dwReserved    = " ) << m_dwReserved        << _T( "\n" );
     roContext << _T( "   m_oTimeGenerated= " ) << m_oTimeGenerated    << _T( "\n" );
     roContext << _T( "   m_oTimeWritten  = " ) << m_oTimeWritten      << _T( "\n" );
     roContext << _T( "   m_dwEventID     = " ) << m_dwEventID         << _T( "\n" );
     roContext << _T( "   m_wEventType    = " ) << m_wEventType        << _T( "\n" );
     roContext << _T( "   m_wEventCategory= " ) << m_wEventCategory    << _T( "\n" );
     roContext << _T( "   m_wPairedEventFlags = " ) << m_wPairedEventFlags << 
         _T( "\n" );
     roContext << _T( "   m_dwClosingRecNum= " ) << m_dwClosingRecNum   <<
         _T( "\n" );
     roContext << _T( "   m_oUserSID      = " ) << m_oUserSID          << _T( "\n" );
     roContext << _T( "   m_oData         = " ) << m_oData             << _T( "\n" );
     roContext << _T( "   m_oStringArray  = " ) << m_oStringArray      << _T( "\n" );
     roContext << _T( "}\n" );
 }
 #endif