System.Diagnostics.EventLog doesn't contain correct message - c#

In some cases, when retrieving event logs from System.Diagnostics.EventLog, message like this
The description for Event ID '10016' in Source 'DCOM' cannot be found...
is returned. I found out that this response is also returned by Get-EventLog command in Powershell.
The actual message should look like this:
The application-specific permission settings do not grant Local Activation permission...
and is returned by Get-WinEvent command.
Is there any way to retrieve the second message in .Net Framework project? (without calling an independent Powershell script)?
UPDATE
I implemented the suggested solution, but now I stumbled on a different problem - how can I retrieve Audit Success and Audit Failure information? The EventLogEntry had an enum that contained them, but EventRecord doesn't
Update 2
I found a way to deal with Audits. EventRecord has a Keywords property, I compared it to StandardEventKeywords enum

As mentioned in the comments, Get-WinEvent uses the EventLogReader class to enumerate the events queried, and then calls EventRecord.FormatDescription() on each resulting record to render the localized message.
Here's a sample console application to fetch and print the rendered message of each of the first 10 Warning (Level=3) events in the Application log:
using System;
using System.Diagnostics.Eventing.Reader;
class Program
{
static void Main(string[] args)
{
// construct an EventLogQuery object from a log path + xpath query
var xpath = "*[System[Level=3]]";
var query = new EventLogQuery("Application", PathType.LogName, xpath);
// instantiate an EventLogReader over the query
var reader = new EventLogReader(query);
// read the events one by one
var counter = 0;
EventRecord record = null;
while ((record = reader.ReadEvent()) is EventRecord && ++counter <= 10)
{
// call FormatDescription() to render the message in accordance with your computers locale settings
var renderedMessage = record.FormatDescription();
Console.WriteLine(renderedMessage);
}
}
}
Beware that it's entirely possible for FormatDescription() to return an empty string - this will occur when the event logging provider didn't provide a message template for the given event id.

Thank you Mathias R. Jessen. Posting your suggestion as an answer to help other community members.
You can try and mimic what Get-WinEvent does: enumerate events with an EventLogReader, then call FormatDescription() on the resulting events to render the message
You can refer to EventLogReader Class and EventRecord.FormatDescription Method

Thanks to Mathias R. Jensen, I created something similar using XML query
static void Main(string[] args)
{
string logName = "System";
string sourceName = "DCOM";
string query = $#"<QueryList>
<Query Id=""0"" Path=""{logName}"">
<Select Path=""{logName}"">
*[System[Provider[#EventSourceName=""{sourceName}""]]]
and
*[System[TimeCreated[#SystemTime>=""{DateTime.UtcNow.AddMinutes(-10).ToString("O")}""]]]
</Select>
</Query>
</QueryList>";
EventLogQuery elq = new EventLogQuery("System", PathType.LogName, query);
EventLogReader elr = new EventLogReader(elq);
EventRecord entry;
while ((entry = elr.ReadEvent()) != null)
{
Console.WriteLine(entry.FormatDescription());
}
Console.ReadLine();
}

Related

Need a Way to Search through Event Logs by RecordID

I am trying to search through a folder with Event Logs in them, eventpath has the path of the specific Event Log I want to access. I want to use a specified RecordID to find it's correlated FormatDescription and display it in a MessageBox. I want to be able to use the eventpath to access each Event Log since I am using 6 separate .evtx files and need to use this method on all of them.
I found this solution, but I get an error when I'm trying to Query. I've tried to find a fix, but it seems as if it's just not going to work for what I need. I commented where exactly it is occurring in the code.
This is the exception: System.Diagnostics.Eventing.Reader.EventLogException: The specified path is invalid.
I can't find a fix for this code, but if anyone knows a fix or another way to approach searching through Event Logs by RecordID and giving the corresponding FormatDescription, it would be greatly appreciated.
I am using C# in Windows Presentation Foundation.
public void getDesc(string recordid)
{
string eventpath = getEventPath();
//takes off the .evtx of the path
string result = eventpath.Substring(0, eventpath.Length - 5);
//result1 is going to be similar to this:
//C:\Users\MyName\AppData\Local\Temp\randomTempDirectory\additional_files\DiagnosticInfo\WindowsEventLogs\Application
string sQuery = "*[System/EventRecordID=" + recordid + "]";
var elQuery = new EventLogQuery(result, PathType.LogName, sQuery);
//this is where it errors out
//error: Specified Channel Path is invalid
using (var elReader = new System.Diagnostics.Eventing.Reader.EventLogReader(elQuery))
{
List<EventRecord> eventList = new List<EventRecord>();
EventRecord eventInstance = elReader.ReadEvent();
try
{
while ((eventInstance = elReader.ReadEvent()) != null)
{
//Access event properties here:
string formatDescription = eventInstance.FormatDescription();
MessageBox.Show(formatDescription);
}
}
finally
{
if (eventInstance != null)
eventInstance.Dispose();
}
}
}

How to get All attributes from an Active Directory user in C#

I have been searching for quite some time for a solution using C# code that can query an Active Directory user for all the attributes it has registered to it, whether or not they have a NULL Value. These attributes are visible through the Attribute editor tab in the properties of the user in ADSI Edit on the domain server.
AD user attributes in ADSI edit
I need to dynamically retrieve these attributes, which means I probably can't reliably get these attribute names through the ADSI documentation on MSDN and because not all of these attributes might be user object specific:
https://msdn.microsoft.com/en-us/library/ms675090(v=vs.85).aspx
Here is what I have tried so far, but only got a fraction of the attributes of the user object:
PS command Get-ADUser -Identity administrator -Properties: This retrieved a good part of the attributes, but not nearly all of them and I do not know what .NET Classes and methods are invoked during this command, since TypeName = Microsoft.ActiveDirectory.Management.ADUser, which does not exist in the .NET framework. How can I see the specific methods that are using from .NET in PS?
C# calling this method:
public bool GetUserAttributes(out List<string> userAttributes, string userName)
{
userAttributes = new List<string>();
var valueReturn = false;
try
{
const string pathNameDomain = "LDAP://test.local";
var directoryEntry = new DirectoryEntry(pathNameDomain);
var directorySearcher = new DirectorySearcher(directoryEntry)
{
Filter = "(&(objectClass=user)(sAMAccountName=" + userName + "))"
};
var searchResults = directorySearcher.FindAll();
valueReturn = searchResults.Count > 0;
StreamWriter writer = new StreamWriter("C:\\LDAPGETUSERADEXAMPLE.txt");
foreach (SearchResult searchResult in searchResults)
{
foreach (var valueCollection in searchResult.Properties.PropertyNames)
{
userAttributes.Add(valueCollection.ToString() + " = " + searchResult.Properties[valueCollection.ToString()][0].ToString());
try
{
writer.WriteLine("Bruger attribut:" + valueCollection);
}
catch (Exception)
{
throw;
}
}
}
C# calling this method:
public List<string> GetADUserAttributes()
{
string objectDn = "CN=testuser,OU=TEST,DC=test,DC=local";
DirectoryEntry objRootDSE = new DirectoryEntry("LDAP://" + objectDn);
List<string> attributes = new List<string>();
foreach (string attribute in objRootDSE.Properties.PropertyNames)
{
attributes.Add(attribute);
}
return attributes;
}
What should I do to not filter out any attributes of the user object I am trying to retrieve from?
I am aware that Active Directory by default will only shows attributes that are default or have a value in them, I am trying to overcome this limitation.
EDIT 1:
I have temporarily postponed the specific question.
I have been trying to benchmark which of these methods are the fastest at retrieving (READ Operation) the SAM account name of 10.000 individual AD users called for example "testuser", the methods I benchmark are the following:
Time to complete: about 500 msec : ADSI - system.directoryservices
Time to complete: about 2700 msec: Principal - searcher system.directoryservices.accountmanagement
Time to complete: about NOT WORKING :LDAP - System.DirectoryServices.Protocols
Time to complete: about 60 msec : SQL - System.Data.SqlClient
I am querying for the user information from a workstation - Windows 10 machine in the domain I am querying. the workstation (4 vcpu), DC (2vpu) and DB (2vcpu) server is run as Hyper V vm's.
All attributes that any class can have are defined in Active Directory Schema
Use this to query for the user class. Then just call GetAllProperties method
var context = new DirectoryContext(DirectoryContextType.Forest, "amber.local");
using (var schema = System.DirectoryServices.ActiveDirectory.ActiveDirectorySchema.GetSchema(context))
{
var userClass = schema.FindClass("user");
foreach (ActiveDirectorySchemaProperty property in userClass.GetAllProperties())
{
// property.Name is what you're looking for
}
}
However AD schema may vary from one AD environment to another. For example, third party programs or Exchange Server may extend schema with custom attributes. It means that the solution with pre-defined columns will work only for a specific environment.

How do I watch the event log for a specific entry in realtime, also getting the XML data?

I'm building an application which should watch file for access, reading, writing, deleting.
I'm using the built in auditing system on a Windows 7 Pro. You turn it on in gpedit.msc, and then set the audit flags for the files you want to watch, and then you get entries in the security log.
What I want to do is watching the security log in real time, which I do like this:
static EventLog securityLog = new EventLog("Security", System.Environment.MachineName);
securityLog.EntryWritten += new EntryWrittenEventHandler(OnEntryWritten);
securityLog.EnableRaisingEvents = true;
This works and calls my OnEntryWritten-Function.
public static void OnEntryWritten(object source, EntryWrittenEventArgs entry)
entry.Entry is the EntryWrittenEventArgs.Entry Property, which doesn't seem to give me any access to the XML-properties of the entry, which I need, beecause it contains additional information.
What I'm trying to do afterwards is to query the event log via another EventLogReader, because I can get entry.Entry.Index which should be the eventInstance.RecordId of an event that I get from the EventLogReader.
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">">*[System[(EventRecordID=181616)]]</Select>
</Query>
</QueryList>
works as XPath query directly in the event log, it gives back just one entry.
string query = "*[System[(EventRecordID=" + entry.Entry.Index + ")]]";
// Create Event Log Query and Reader
EventLogQuery eventsQuery = new EventLogQuery("Security",
PathType.LogName,
query);
EventLogReader logReader = new EventLogReader(eventsQuery);
// For each event returned from the query
for (EventRecord eventInstance = logReader.ReadEvent(); eventInstance != null; eventInstance = logReader.ReadEvent())
{
if (eventInstance.RecordId == entry.Entry.Index) //RecordId and Index are the same thing: the identifier of the record/entry.
{
XDocument xml;
try
{
xml = XDocument.Parse(logReader.ReadEvent().ToXml());
}
catch (Exception e)
{
//logger.Write(e.Message.ToString());
break; //We seem to have a newline character in the logReader.ReadEvent() sometimes, but nothing else, so we can safely break here or completely ignore it.
}
This fails when I try to get the xml, why is that?
I get an "Object reference not set to an instance of an object." which is an System.NullReferenceException. I'm not sure how this error actually can happen.
If I query the log like this
EventLogQuery eventsQuery = new EventLogQuery("Security",
PathType.LogName,
"*[EventData[Data[#Name='ObjectType'] and (Data='File')]] ");
it works without a problem.
What's the best way to do this, anyway?
The InstanceId does not return the same value as the index value.
Try the following snippet to get the correct ID
UInt16 eventid = (UInt16)(entry.Entry.InstanceId)
Have you checked the output of XDocument.Parse(logReader.ReadEvent().ToXml()) ? Does ToXml() generate a proper XDocument (with header, root element...)? That might be the problem.
However if you don't stick to this solution, you could try to use FileSystemWatcher or the official file monitoring tool from Microsoft: Filemon
Every time you call ReadEvent() you are retrieving the next event. Your query only returns one event. The line
xml = XDocument.Parse(logReader.ReadEvent().ToXml());
is the culprit.
Your code should look more like this:
string query = "*[System[(EventRecordID=" + entry.Entry.Index + ")]]";
// Create Event Log Query and Reader
EventLogQuery eventsQuery = new EventLogQuery("Security",
PathType.LogName,
query);
EventLogReader logReader = new EventLogReader(eventsQuery);
EventRecord eventInstance = logReader.ReadEvent();
if (eventInstance != null)
{
XDocument xml;
try
{
xml = XDocument.Parse(eventInstance.ToXml());
}
catch (Exception e)
{
//This probably won't happen now.
break; //We seem to have a newline character in the
}
}

DynamicActivity - How to invoke a workflow that stored in Database?

This is proof of concept project - The goal is to create an application that receive some system wide events and based on some business rules invokes a specific workflow.
The workflows are created separately and the xaml source is stored in a database.
Following is the code that used to invoke the workflow:
public void RaiseEvent(IEvent e, IEventData eventData)
{
var typeName = e.GetType().FullName;
// Query Db for all workflows for the event
var repo = new WorkflowRepository();
var workflows = repo.GetActiveWorkflowsByEvent(typeName);
foreach (var wf in workflows)
{
var condition =
ConditionEvaluator.PrepareCondition(wf.Condition.Expression, eventData);
var okToStart = ConditionEvaluator.Evaluate(condition);
if (okToStart)
{
// Next line is throwing an exeption
object o = XamlServices.Parse(wf.WorkflowDefinition.Expression);
DynamicActivity da = o as DynamicActivity;
WorkflowInvoker.Invoke(da,
new Dictionary<string, object>
{{ "EventData", eventData }});
}
}
We have created very simple workflow that runs without problems on its own. But when xaml is being loaded using XamlService.Parse it throw following exception:
System.Xaml.XamlObjectWriterException was unhandled
Message='No matching constructor found on type 'System.Activities.Activity'.
You can use the Arguments or FactoryMethod directives to construct this type.'
Line number '1' and line position '30'.
Any idea what is wrong?
Thank you.
Not sure what is causing your problem, I have used XamlServices.Load() in the past without any problems, but the easiest way of loading a workflow XAML at runtime is by using the ActivityXamlServices.Load(). See here for an example.
Ok I have solved this by using ActivityXamlServices
So Instead of this line:
object o = XamlServices.Parse(wf.WorkflowDefinition.Expression);
I am using following snippet:
var mStream = new memoryStream(
ASCIIEncoding.Default.GetBytes(wf.WorkflowDefinition.Expression));
object o = ActivityXamlServices.Load(mStream);

How to Query for an event log details with a given event id?

How to know whether a particular event (given event ID, time and node as inputs) is logged or not? [In this case, I know only one event will be logged]
If the event is logged, how do I get details like event description, Log-name etc..
for eg, I want to query for an event under the node Applications and Services Logs > Microsoft > Windows > groupPolicy > Operational, and event id is 5315 and time is current time.
There are a few new twists if your going to query events from the new style Windows EventLogs.
You will have to use the classes from the System.Diagnostics.Eventing.Reader namespace to read the new events.
Your query will be in Xpath form, so that time value is tricky, see msdn for the EventLogQuery definition.
Your program will run into access issues, be ready to impersonate a user that's included in the EventReaders AD group on the logging machine.
This sample shows some of the new access methods:
string eventID = "5312";
string LogSource = "Microsoft-Windows-GroupPolicy/Operational";
string sQuery = "*[System/EventID=" + eventID + "]";
var elQuery = new EventLogQuery(LogSource, PathType.LogName, sQuery);
using (var elReader = new System.Diagnostics.Eventing.Reader.EventLogReader(elQuery))
{
List<EventRecord> eventList = new List<EventRecord>();
EventRecord eventInstance = elReader.ReadEvent();
try
{
for (null != eventInstance; eventInstance = elReader.ReadEvent())
{
//Access event properties here:
//eventInstance.LogName;
//eventInstance.ProviderName;
eventList.Add(eventInstance);
}
}
finally
{
if (eventInstance != null)
eventInstance.Dispose();
}
}
You could query the event log in question:
var sourceName = "MySource";
var el = new EventLog("Application");
var latestEntryTime = (from entry in el.Entries.Cast<EventLogEntry>()
where entry.Source == sourceName
&& // put other where clauses here...
orderby entry.TimeWritten descending
select entry).First();
However, be warned that this approach is slow, since the Entries collection tends to be quite big.

Categories

Resources