Filtering Event Log Entries by Event Code

When you filter event log entries bu event code using WMI, you can run a query like the following:

"SELECT Message FROM Win32_NTLogEvent WHERE EventCode = 7"

And as expected, this will return the entries with event code 7. Now, let’s look at the corresponding C# code, given that you already have a list of event log entries, you explore which property you should use to filter the list using LINQ. Conveniently, you find EventLogEntry.EventID, but it’s obsolete and the compiler warning asks you to use EventLogEntry.InstanceId instead. So, you follow the instructions and do so:

 IEnumerable<EventLogEntry> filtered = entries.Where(e => e.InstanceId == 7);

You run the code, and test it; everything works; you don’t think about it twice.

Later on, you inspect certain event log entries, and you find out that your filter didn’t catch them. So, you feel suspicious and debug the code, only to find out that InstanceId returns 3221487623, even though the same entry is showing 7 as its event code in the event viewer.

What’s going on here?

It turns out, the answer was in the remarks section on MSDN:

The EventID property equals the InstanceId with the top two bits masked off. Two event log entries from the same source can have matching EventID values, but have different InstanceId values due to differences in the top two bits of the resource identifier.

Digging deeper, the obsolete EventID property only masks the top two bits, but there’s more we don’t care about in that case, according to MSDN again (see Event Identifiers):

Event ID

So to really filter by just the event code, the code will look like the following:

 IEnumerable<EventLogEntry> filtered = entries.Where(e => (e.InstanceId & 0xFFFF) == 7);

 

Why didn’t my test catch that?

Because your test wrote the event log entry using the straight-forward WriteEntry method; the remarks section explains:

If the application wrote the event entry using one of the WriteEntry methods, the InstanceId property matches the optional eventId parameter. If the application wrote the event using WriteEvent, the InstanceId property matches the resource identifier specified in the InstanceId of the instance parameter. If the application wrote the event using the Win32 API ReportEvent, the InstanceId property matches the resource identifier specified in the dwEventID parameter.

It's worth noting that using an EventLogQuery doesn't reproduce this issue.