Learn how to enumerate local and remote event logs, instantiate an EventLog object for a specific event log, create a custom event log specific to your application, and delete an event log.
My previous article illustrated how easy it is to access and modify the Windows Registry using the .NET Framework. Continuing with the theme of accessing Windows subsystems, this week's article shows how you can give your Managed C++ Windows applications a more polished, professional look by using the Windows Event Log to record warning, error, and informational messages.
Working with the Event Log involves quite a few tasks, so this series is divided among several parts. This article illustrates how to accomplish the following Event Log tasks:
- Enumerate the logs on the local or any connected remote machine (for which the application has sufficient rights)
- Instantiate an EventLog object for a specific local or remote log
- Create your own custom event logs that are specific to your application
- Delete event logs
Future articles will cover how to read and write to the event log (using versions 1.1 and 2.0 of the .NET Framework) and how to monitor the event log for changes.
Important notes before continuing:
- Because the Event Log is a Windows service, this article pertains only to Windows NT, 2000, XP, 2003, and Longhorn.
- I could write quite a bit about the Event Log in terms of what it is and why I believe it's a much better means of recording application messages than using log files. However, I won't get too much into philosophical issues and instead focus on code. If you are interested in this information, drop me an e-mail and I'll post an excerpt from my book Extending MFC Applications with the .NET Framework where I cover these issues.
A (Very Quick) Briefing on the Event Log and Viewer
The Windows Event Log is a service that starts when Windows loads. It is used as a central repository for applications to record messages related to the success or failure of their respective tasks. There are three standard, system-supplied logs: Application, Security, and System. Windows also supplies an application for viewing and modifying the logs. You can access this application (called the Event Viewer) via the Windows menu system or by typing eventvwr at the Run prompt. The Event Viewer allows you to search, filter, create views on, back up, and restore event log messages.
Applications (event sources) record messages (events) into a specified log. The exact log an event source uses typically depends on the type of application recording the event. For example, the System event log is used by services and kernel mode programs such as device drivers. The Security event log is used by applications that need to record log-on attempts, suspicious port scans, and other security events. (Note that the Security event log restricts normal-mode applications to read-only access for obvious reasons.) Finally, the Application event log is used by the majority of user-mode Windows applications. In addition to these logs, you also can define your own logs (called custom event logs), which this article also covers.
Instantiating EventLog Objects and Enumerating Event Logs
The main .NET class that you will work with when accessing the Event Log from a Managed C++ application is the Diagnostics::EventLog class. You can construct an EventLog object in the following two ways:
- Calling one of the EventLog overloaded constructors where you specify such information as the log name, machine name, and the event source name
- Calling the static EventLog::GetEventLogs method, which returns all logs for which the user has permissions on either the local machine or the specified remote machine
When specifying a log name, realize that log names are not case-sensitive. However, the .NET runtime will throw a System::IO::IOException exception if you specify a log name that is not found. Also note that the local machine can be designated via passing a period (".") as the machine name:
// Get local machine's Application log
EventLog* el = new EventLog(S"application");
// Get Application log from the "myserver"
EventLog* e2 = new EventLog(S"application", S"myserver");
// Get Application log from the local machine for the event source
// named "my application"
EventLog* e3 = new EventLog(S"application", S".", S"my application");
To retrieve an array of all of the logs, call the EventLog::GetEventLogs method, where you can pass a string value specifying the machine. Remember that passing a value of "." indicates the local machine, as does calling the parameterless version of this method:
EventLog* logs = EventLog::GetEventLogs();
for (int i = 0; i < logs->Count; i++)
Creating Custom Event Logs
As mentioned earlier, you also have the ability to programmatically create your own custom event logs. (Figure 1 shows a screen shot of my Event Viewer where I've created a custom event log called "My Application Log".) You typically use event logs in situations where you wish to keep your application's events separate from other event sources.
Figure 1: You can use the Windows Event Viewer to work with both system-supplied as well as custom event logs.
What's the advantage to defining a custom event log for your application? The main reason is support. Because the Event Viewer allows a user to export a log's events, the user is able to export only the events related to your application rather than an entire log containing events from other event sources. (The Event Viewer's filter function affects only what can be viewed; all events are exported when saving a log, regardless of the user-applied filter.)
Custom event logs are associated with specific event sources in a one-to-one relationship and are created via the static EventLog::CreateEventSource method. Because creating a custom event log entails also verifying if the event log already exists and deleting the event source if it's associated with another event log (event sources can be associated only with a single log), I list here a generic method for doing everything you need to create a custom event log:
// Method assumes caller will catch exceptions
// thrown by EventLog class
void CreateCustomEventLog(String* eventSource, String* logName)
// Does the Log already exist?
// Does the event source already exist?
// Delete the event source because it can
// only be associated with one log
// Create the event source and associate it
// with the new custom log.
Only the first eight (8) characters of a custom log are significant. Therefore, when programmatically creating custom logs, you need to ensure that the log names are unique within the first eight characters or a System::ArgumentException will be thrown.
By using the CreateCustomEventMethod method, you can create your custom event logs like this:
// Create a custom event log called "My Application Log" for the
// event source "My Application"
CreateCustomEventLog(S"My Application", S"My Application Log");
Deleting Event Logs
Logs are programmatically deleted via the static EventLog::Delete method, where you pass the name of the log (and optionally, the machine name). As you can delete the system-supplied logs, be cautious when you use this method:
// Delete my custom event log
EventLog::Delete(S"My Application Log");
Who Has Access to My Event Log?
Each application that is defined as an event source capable of recording events to an event log is listed in the Windows Registry. You can see this by viewing the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\<LogName> Registry (where LogName is either one of the system-supplied event logs (Application, System, or Security) or the name of a custom event log. Figure 2 is a screen capture where I've selected the custom event log "My Application Log".
Figure 2: All event logs and event log sources are stored in the Registry in the HKLM hive.
Note that the event log subkey has a value called Sources. This value is a space-delimited string defining every event source that can write to that particular log. In addition, you can expand the event log subkey to see that each event source is also defined in its own subkey (below the event log subkey), where values such as EventMessageFile and TypesSupported are stored.
As I mentioned at the outset of this article, I've divided this topic among several articles that each focus on a logically ordered task dealing with the Event Log. This article dealt with the event log tasks: enumerating local and remote event logs, instantiating an EventLog object for a specific local or remote event log, creating a custom event log, and deleting an event log. Upcoming articles will explore how to programmatically record and read events from an event log (using both 1.1 and 2.0 .NET functionality) and code an event log monitoring application.