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(); } } }