using System;
using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader;
namespace Microsoft.Win32.TaskScheduler
{
///
/// Historical event information for a task.
///
public sealed class TaskEvent : IComparable
{
internal TaskEvent(EventRecord rec)
{
this.EventId = rec.Id;
this.EventRecord = rec;
this.Version = rec.Version;
this.TaskCategory = rec.TaskDisplayName;
this.OpCode = rec.OpcodeDisplayName;
this.TimeCreated = rec.TimeCreated;
this.RecordId = rec.RecordId;
this.ActivityId = rec.ActivityId;
this.Level = rec.LevelDisplayName;
this.UserId = rec.UserId;
this.ProcessId = rec.ProcessId;
this.TaskPath = rec.Properties.Count > 0 ? rec.Properties[0].Value.ToString() : null;
}
///
/// Gets the activity id.
///
public Guid? ActivityId
{
get; internal set;
}
///
/// Gets the data value from the task specific event data item list.
///
/// The name of the data element.
/// Contents of the requested data element if found. null if no value found.
///
///
/// EventIDPossible elements
/// 100 - Task Started
///
/// TaskNameFull path of task.
/// UserContextUser account.
/// InstanceIdTask instance identifier.
///
///
/// 101 - Task Start Failed Event
///
/// TaskNameFull path of task.
/// UserContextUser account.
/// ResultCodeError code for failure.
///
///
/// 102 - Task Completed
///
/// TaskNameFull path of task.
/// UserContextUser account.
/// InstanceIdTask instance identifier.
///
///
/// 103 - Task Failure Event
///
/// TaskNameFull path of task.
/// UserContextUser account.
/// InstanceIdTask instance identifier.
/// ResultCodeError code for failure.
///
///
/// 106 - Task Registered Event
///
/// TaskNameFull path of task.
/// UserContextUser account.
///
///
/// 107 - Time Trigger Event
///
/// TaskNameFull path of task.
/// InstanceIdTask instance identifier.
///
///
/// 108 - Event Trigger Event
///
/// TaskNameFull path of task.
/// InstanceIdTask instance identifier.
///
///
/// 109 - Task Registration Trigger Event
///
/// TaskNameFull path of task.
/// InstanceIdTask instance identifier.
///
///
/// 110 - Task Run Event
///
/// TaskNameFull path of task.
/// InstanceIdTask instance identifier.
///
///
/// 111 - Task Termination Event
///
/// TaskNameFull path of task.
/// InstanceIdTask instance identifier.
///
///
/// 114 - Missed Task Launch Event
///
/// TaskNameFull path of task.
/// InstanceIdTask instance identifier.
///
///
/// 117 - Idle Trigger Event
///
/// TaskNameFull path of task.
/// InstanceIdTask instance identifier.
///
///
/// 118 - Boot Trigger Event
///
/// TaskNameFull path of task.
/// InstanceIdTask instance identifier.
///
///
/// 119 - Logon Trigger Event
///
/// TaskNameFull path of task.
/// UserNameUser account.
/// InstanceIdTask instance identifier.
///
///
/// 126 - Failed Task Restart Event
///
/// TaskNameFull path of task.
/// ResultCodeError code for failure.
///
///
/// 129 - Created Task Process Event
///
/// TaskNameFull path of task.
/// PathTask engine.
/// ProcessIDProcess ID.
/// PriorityProcess priority.
///
///
/// 135 - Task Not Started Without Idle
///
/// TaskNameFull path of task.
///
///
/// 140 - Task Updated
///
/// TaskNameFull path of task.
/// UserNameUser account.
///
///
/// 141 - Task deleted
///
/// TaskNameFull path of task.
/// UserNameUser account.
///
///
/// 142 - Task disabled
///
/// TaskNameFull path of task.
/// UserNameUser account.
///
///
/// 200 - Action start
///
/// TaskNameFull path of task.
/// ActionNamePath of executable.
/// TaskInstanceIdTask instance ID.
/// EnginePIDEngine process ID.
///
///
/// 201 - Action success
///
/// TaskNameFull path of task.
/// ActionNamePath of executable.
/// TaskInstanceIdTask instance ID.
/// ResultCodeExecutable result code.
/// EnginePIDEngine process ID.
///
///
/// 202 - Action failure
///
/// TaskNameFull path of task.
/// ActionNamePath of executable.
/// TaskInstanceIdTask instance ID.
/// ResultCodeExecutable result code.
/// EnginePIDEngine process ID.
///
///
/// 203 - Action launch failure
///
/// TaskNameFull path of task.
/// ActionNamePath of executable.
/// TaskInstanceIdTask instance ID.
/// ResultCodeExecutable result code.
///
///
/// 204 - Action launch failure
///
/// TaskNameFull path of task.
/// ActionNamePath of executable.
/// TaskInstanceIdTask instance ID.
/// ResultCodeExecutable result code.
///
///
/// 301 - Task engine exit event
///
/// TaskEngineNamePermissions and priority for engine process.
///
///
/// 310 - Task engine process started
///
/// TaskEngineNamePermissions and priority for engine process.
/// CommandEngine path.
/// ProcessIDProcess ID.
/// ThreadIDThread ID.
///
///
/// 314 - Task engine idle
///
/// TaskEngineNamePermissions and priority for engine process.
///
///
///
///
public string GetDataValue(string name)
{
var propsel = new System.Diagnostics.Eventing.Reader.EventLogPropertySelector(new string[] { string.Format("Event/EventData/Data[@Name='{0}']", name) });
try
{
var logEventProps = ((EventLogRecord)this.EventRecord).GetPropertyValues(propsel);
return logEventProps[0].ToString();
}
catch { }
return null;
}
///
/// Gets the event id.
///
public int EventId
{
get; internal set;
}
///
/// Gets the underlying .
///
public EventRecord EventRecord
{
get; internal set;
}
///
/// Gets the level.
///
public string Level
{
get; internal set;
}
///
/// Gets the op code.
///
public string OpCode
{
get; internal set;
}
///
/// Gets the process id.
///
public int? ProcessId
{
get; internal set;
}
///
/// Gets the record id.
///
public long? RecordId
{
get; internal set;
}
///
/// Gets the task category.
///
public string TaskCategory
{
get; internal set;
}
///
/// Gets the task path.
///
public string TaskPath
{
get; internal set;
}
///
/// Gets the time created.
///
public DateTime? TimeCreated
{
get; internal set;
}
///
/// Gets the user id.
///
public System.Security.Principal.SecurityIdentifier UserId
{
get; internal set;
}
///
/// Gets the version.
///
public byte? Version
{
get; internal set;
}
///
/// Returns a that represents this instance.
///
///
/// A that represents this instance.
///
public override string ToString()
{
return EventRecord.FormatDescription();
}
///
/// Compares the current object with another object of the same type.
///
/// An object to compare with this object.
///
/// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the other parameter.Zero This object is equal to other. Greater than zero This object is greater than other.
///
public int CompareTo(TaskEvent other)
{
int i = this.TaskPath.CompareTo(other.TaskPath);
if (i == 0)
{
i = this.ActivityId.ToString().CompareTo(other.ActivityId.ToString());
if (i == 0)
i = Convert.ToInt32(this.RecordId - other.RecordId);
}
return i;
}
}
///
/// An enumerator over a task's history of events.
///
public sealed class TaskEventEnumerator : IEnumerator, IDisposable
{
private EventRecord curRec;
private EventLogReader log;
internal TaskEventEnumerator(EventLogReader log)
{
this.log = log;
}
///
/// Gets the element in the collection at the current position of the enumerator.
///
///
/// The element in the collection at the current position of the enumerator.
///
public TaskEvent Current
{
get { return new TaskEvent(curRec); }
}
///
/// Gets the element in the collection at the current position of the enumerator.
///
///
/// The element in the collection at the current position of the enumerator.
///
object System.Collections.IEnumerator.Current
{
get { return this.Current; }
}
///
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///
public void Dispose()
{
log.CancelReading();
log.Dispose();
log = null;
}
///
/// Advances the enumerator to the next element of the collection.
///
///
/// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
///
///
/// The collection was modified after the enumerator was created.
///
public bool MoveNext()
{
return (curRec = log.ReadEvent()) != null;
}
///
/// Sets the enumerator to its initial position, which is before the first element in the collection.
///
///
/// The collection was modified after the enumerator was created.
///
public void Reset()
{
log.Seek(System.IO.SeekOrigin.Begin, 0L);
}
///
/// Seeks the specified bookmark.
///
/// The bookmark.
/// The offset.
public void Seek(EventBookmark bookmark, long offset = 0L)
{
log.Seek(bookmark, offset);
}
///
/// Seeks the specified origin.
///
/// The origin.
/// The offset.
public void Seek(System.IO.SeekOrigin origin, long offset)
{
log.Seek(origin, offset);
}
}
///
/// Historical event log for a task. Only available for Windows Vista and Windows Server 2008 and later systems.
///
public sealed class TaskEventLog : IEnumerable
{
private EventLogQuery q;
private const string TSEventLogPath = "Microsoft-Windows-TaskScheduler/Operational";
///
/// Initializes a new instance of the class.
///
/// The task path. This can be retrieved using the property.
/// Thrown when instantiated on an OS prior to Windows Vista.
public TaskEventLog(string taskPath) : this(".", taskPath)
{
Initialize(".", BuildQuery(taskPath), true);
}
///
/// Initializes a new instance of the class.
///
/// Name of the machine.
/// The task path. This can be retrieved using the property.
/// The domain.
/// The user.
/// The password.
/// Thrown when instantiated on an OS prior to Windows Vista.
public TaskEventLog(string machineName, string taskPath, string domain = null, string user = null, string password = null)
{
Initialize(machineName, BuildQuery(taskPath), true, domain, user, password);
}
///
/// Initializes a new instance of the class that looks at all task events from a specified time.
///
/// The start time.
/// Name of the task.
/// Name of the machine (optional).
/// The domain.
/// The user.
/// The password.
public TaskEventLog(DateTime startTime, string taskName = null, string machineName = null, string domain = null, string user = null, string password = null)
{
int[] numArray = new int[] { 100, 102, 103, 107, 108, 109, 111, 117, 118, 119, 120, 121, 122, 123, 124, 125 };
Initialize(machineName, BuildQuery(taskName, numArray, startTime), false, domain, user, password);
}
///
/// Initializes a new instance of the class.
///
/// Name of the task.
/// The event i ds.
/// The start time.
/// Name of the machine (optional).
/// The domain.
/// The user.
/// The password.
public TaskEventLog(string taskName = null, int[] eventIDs = null, DateTime? startTime = null, string machineName = null, string domain = null, string user = null, string password = null)
{
Initialize(machineName, BuildQuery(taskName, eventIDs, startTime), true, domain, user, password);
}
internal static string BuildQuery(string taskName = null, int[] eventIDs = null, DateTime? startTime = null)
{
const string queryString =
"" +
" " +
" " +
" " +
"";
const string OR = " or ";
const string AND = " and ";
System.Text.StringBuilder sb = new System.Text.StringBuilder("*");
if (eventIDs != null && eventIDs.Length > 0)
{
if (sb.Length > 1) sb.Append(AND);
sb.AppendFormat("({0})", string.Join(OR, Array.ConvertAll(eventIDs, i => string.Format("EventID={0}", i))));
}
if (startTime.HasValue)
{
if (sb.Length > 1) sb.Append(AND);
sb.AppendFormat("TimeCreated[@SystemTime>='{0}']", System.Xml.XmlConvert.ToString(startTime.Value, System.Xml.XmlDateTimeSerializationMode.RoundtripKind));
}
if (sb.Length > 1)
{
sb.Insert(1, "[System[Provider[@Name='Microsoft-Windows-TaskScheduler'] and ");
sb.Append("]");
}
if (!string.IsNullOrEmpty(taskName))
{
if (sb.Length == 1)
sb.Append("[");
else
sb.Append("]" + AND + "*[");
sb.AppendFormat("EventData[Data[@Name='TaskName']='{0}']", taskName);
}
if (sb.Length > 1)
sb.Append("]");
return string.Format(queryString, sb.ToString());
}
private void Initialize(string machineName, string query, bool revDir, string domain = null, string user = null, string password = null)
{
if (System.Environment.OSVersion.Version.Major < 6)
throw new NotSupportedException("Enumeration of task history not available on systems prior to Windows Vista and Windows Server 2008.");
System.Security.SecureString spwd = null;
if (password != null)
{
spwd = new System.Security.SecureString();
int l = password.Length;
foreach (char c in password.ToCharArray(0, l))
spwd.AppendChar(c);
}
q = new EventLogQuery(TSEventLogPath, PathType.LogName, query) { ReverseDirection = revDir };
if (machineName != null && machineName != "." && !machineName.Equals(Environment.MachineName, StringComparison.InvariantCultureIgnoreCase))
q.Session = new EventLogSession(machineName, domain, user, spwd, SessionAuthentication.Default);
}
///
/// Gets the total number of events for this task.
///
public long Count
{
get
{
using (EventLogReader log = new EventLogReader(q))
{
long seed = 64L, l = 0L, h = seed;
while (log.ReadEvent() != null)
log.Seek(System.IO.SeekOrigin.Begin, l += seed);
bool foundLast = false;
while (l > 0L && h >= 1L)
{
if (foundLast)
l += (h /= 2L);
else
l -= (h /= 2L);
log.Seek(System.IO.SeekOrigin.Begin, l);
foundLast = (log.ReadEvent() != null);
}
return foundLast ? l + 1L : l;
}
}
}
///
/// Gets or sets a value indicating whether to enumerate in reverse when calling the default enumerator (typically with foreach statement).
///
///
/// true if enumerates in reverse (newest to oldest) by default; otherwise, false to enumerate oldest to newest.
///
[System.ComponentModel.DefaultValue(false)]
public bool EnumerateInReverse { get; set; }
///
/// Returns an enumerator that iterates through the collection.
///
///
/// A that can be used to iterate through the collection.
///
public IEnumerator GetEnumerator()
{
return GetEnumerator(EnumerateInReverse);
}
///
/// Returns an enumerator that iterates through the collection.
///
/// if set to true reverse.
///
/// A that can be used to iterate through the collection.
///
public IEnumerator GetEnumerator(bool reverse)
{
q.ReverseDirection = !reverse;
return new TaskEventEnumerator(new EventLogReader(q));
}
///
/// Returns an enumerator that iterates through a collection.
///
///
/// An object that can be used to iterate through the collection.
///
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}