September 24, 2020
Hot Topics:

Managed C++: Reading and Writing Windows Event Log Entries

  • By Tom Archer
  • Send Email »
  • More Articles »
My previous article illustrated various tasks regarding the Windows Event Log service, including how to enumerate local and remote event logs, instantiate an EventLog object for a specific local or remote log, create a custom event log for your application's logging needs, and delete an event log. This article continues showing how to programmatically work with the Event Log by covering how to enumerate an event log, read specific event entries, and record new events.

As the Event Log is a Windows service, this article pertains only to Windows NT, 2000, XP, 2003 and Longhorn.

Reading Event Log Entries

Reading event logs does not require an explicit call to a member method. This is because any time you associate an EventLog object with an event log—either through the object's constructor or via the EventLog::Log property—the EventLog::Entries array automatically fills with all event entries for that log. In the following snippet, both the log1 and log2 objects contain the event entries for the specified event log. Note that even if you've previously set the EventLog::Log property, setting it to another event log name causes the Entries array to re-initialize with the new event log's events.
// Get all events for "My Application Log"
EventLog* log1 = new EventLog(S"My Application Log");

// Get all events for "Application"
EventLog* log2 = new EventLog();
log2->Log = S"Application");

// Now get the "System" events
log2->Log = S"System";

Once the EventLog::Entries array has been filled, you can easily enumerate its entries—each encapsulated by the EventLogEntry class—via an enumerator. The following EnumerateLogEntries method takes a log name and machine name as its parameters and outputs all of the event log's entries:

// Method assumes caller will catch exceptions 
// thrown by EventLog class
void EnumerateLogEntries(String* logName, String* machine = S".")
#pragma push_macro("new")
#undef new
  EventLog* log = new EventLog(logName, machine);
  System::Collections::IEnumerator* entries = log->Entries->GetEnumerator();
  while (entries->MoveNext())
    EventLogEntry* entry = static_cast<EventLogEntry*>(entries->Current);
    Console::WriteLine("Message: {0}", entry->Message);
    Console::WriteLine("\tType: {0}", __box(entry->EntryType));
    Console::WriteLine("\tEventID: {0}", __box(entry->EventID));
    Console::WriteLine("\tCat: {0}", entry->Category);
    Console::WriteLine("\tCatNbr: {0}", __box(entry->CategoryNumber));
#pragma pop_macro("new")

When instantiating an EventLog object, if you specify a log name and not a machine name, the local computer (".") is assumed.

Unfortunately, there doesn't seem to be a way to filter events as the user can in the Windows Event Viewer application. Therefore, you'll need to perform any needed filtering by manually comparing EventLogEntry properties to the desired values. Here are some examples of manually filtering for the desired events:

// Want only events with an EventID of 100
if (entry->EventID == 100)

// Want only Error events 
if (entry->EntryType 
== EventLogEntryType::Error)

// Want only events from the event 
// source named "My Application"
if (0 == String::Compare(entry->Source, 
                         S"My Application"))

Note that the EventLog::EventID is an obsolete member as of .NET 2.0.

In addition to reading the event entries of an event log using an enumerator, you can read them by specifying their relative index value, as the EventLog::Entries member is an array. As the array is ordered by the date/time the event was recorded, you would need to read from the end of the array to read the last events. To illustrate that, the following method (ReadLastEventEntries) reads the last five entries (EventLogEntry objects) of the specified event log and outputs each entry's TimeGenerated and Message properties:

// Method assumes caller will catch exceptions 
// thrown by EventLog class
void ReadLastEventEntries(String* logName, String* machine = S".")
#pragma push_macro("new")
#undef new
  EventLog* log = new EventLog(logName, machine);

  Console::WriteLine("Reading 5 entries for {0}/{1} event log", 
                     (0 == machine->CompareTo(S".")) 
                        ? Environment::MachineName : machine,

  String* format = S"{0, -10} {1}";
  Console::WriteLine(format, S"DateTime", S"Message");

  EventLogEntry* entry;
  Int32 nEntries = log->Entries->Count;
  for (int i = nEntries; 
       i > Math::Max(0, nEntries - 5);
    entry = log->Entries->Item[i-1];
#pragma pop_macro("new")

As an aside, note the use of the Environment::MachineName property in order to obtain the local machine's name. I use this value because the EventLog::MachineName isn't automatically converted to the local machine name when a "." is passed, although that value does internally represent the local machine.

Because the EventLogEntry property is read-only, you cannot modify an entry or write to the log using the Entries array. Instead, you must use the EventLog::WriteEntry method, which the next section covers.

Page 1 of 2

This article was originally published on December 28, 2004

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date