using JetBrains.Annotations; using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text.RegularExpressions; namespace Microsoft.Win32.TaskScheduler { /// /// Collection of running tasks in a . This class has no public constructor and can only be accessed via the /// properties and functions within . /// public sealed class RunningTaskCollection : IReadOnlyList, IDisposable { private readonly TaskService svc; private readonly V2Interop.IRunningTaskCollection v2Coll; internal RunningTaskCollection([NotNull] TaskService svc) => this.svc = svc; internal RunningTaskCollection([NotNull] TaskService svc, [NotNull] V2Interop.IRunningTaskCollection iTaskColl) { this.svc = svc; v2Coll = iTaskColl; } /// Gets the number of registered tasks in the collection. public int Count { get { if (v2Coll != null) return v2Coll.Count; var i = 0; var v1Enum = new V1RunningTaskEnumerator(svc); while (v1Enum.MoveNext()) i++; return i; } } /// Gets the specified running task from the collection. /// The index of the running task to be retrieved. /// A instance. public RunningTask this[int index] { get { if (v2Coll != null) { var irt = v2Coll[++index]; return new RunningTask(svc, TaskService.GetTask(svc.v2TaskService, irt.Path), irt); } var i = 0; var v1Enum = new V1RunningTaskEnumerator(svc); while (v1Enum.MoveNext()) if (i++ == index) return v1Enum.Current; throw new ArgumentOutOfRangeException(nameof(index)); } } /// Releases all resources used by this class. public void Dispose() { if (v2Coll != null) Marshal.ReleaseComObject(v2Coll); } /// Gets an IEnumerator instance for this collection. /// An enumerator. public IEnumerator GetEnumerator() { if (v2Coll != null) return new ComEnumerator(() => v2Coll.Count, (object o) => v2Coll[o], o => { V2Interop.IRegisteredTask task = null; try { task = TaskService.GetTask(svc.v2TaskService, o.Path); } catch { } return task == null ? null : new RunningTask(svc, task, o); }); return new V1RunningTaskEnumerator(svc); } /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() => $"RunningTaskCollection; Count: {Count}"; System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); private class V1RunningTaskEnumerator : IEnumerator { private readonly TaskService svc; private readonly TaskCollection.V1TaskEnumerator tEnum; internal V1RunningTaskEnumerator([NotNull] TaskService svc) { this.svc = svc; tEnum = new TaskCollection.V1TaskEnumerator(svc); } public RunningTask Current => new RunningTask(svc, tEnum.ICurrent); object System.Collections.IEnumerator.Current => Current; /// Releases all resources used by this class. public void Dispose() => tEnum.Dispose(); public bool MoveNext() => tEnum.MoveNext() && (tEnum.Current?.State == TaskState.Running || MoveNext()); public void Reset() => tEnum.Reset(); } } /// /// Contains all the tasks that are registered within a . This class has no public constructor and can only be /// accessed via the properties and functions within . /// /// /// Potentially breaking change in 1.6.2 and later where under V1 the list previously included the '.job' extension on the task name. /// This has been removed so that it is consistent with V2. /// /// /// public class Program /// { /// bool RootFolderHasTask(string taskName) /// { /// if (TaskService.Instance.RootFolder.Tasks.Count > 0) /// { /// return TaskService.Instance.RootFolder.Tasks.Exists(taskName); /// } /// return false; /// } /// /// TaskCollection GetRootTasksStartingWith(string value) /// { /// var pattern = $"^{Regex.Escape(value)}.*$"; /// return TaskService.Instance.RootFolder.GetTasks(new Regex(pattern)); /// } /// /// public static void Main() /// { /// foreach (var task in GetRootTasksStartingWith("MyCo")) /// if (RootFolderHasTask(task.Name)) /// Console.WriteLine(task.Name); /// } /// } /// [PublicAPI] public sealed class TaskCollection : IReadOnlyList, IDisposable { private readonly TaskFolder fld; private readonly TaskService svc; private readonly V2Interop.IRegisteredTaskCollection v2Coll; private Regex filter; private V1Interop.ITaskScheduler v1TS; internal TaskCollection([NotNull] TaskService svc, Regex filter = null) { this.svc = svc; Filter = filter; v1TS = svc.v1TaskScheduler; } internal TaskCollection([NotNull] TaskFolder folder, [NotNull] V2Interop.IRegisteredTaskCollection iTaskColl, Regex filter = null) { svc = folder.TaskService; Filter = filter; fld = folder; v2Coll = iTaskColl; } /// Gets the number of registered tasks in the collection. public int Count { get { var i = 0; if (v2Coll != null) { var v2Enum = new V2TaskEnumerator(fld, v2Coll, filter); while (v2Enum.MoveNext()) i++; } else { var v1Enum = new V1TaskEnumerator(svc, filter); return v1Enum.Count; } return i; } } /// Gets or sets the regular expression filter for task names. /// The regular expression filter. private Regex Filter { get => filter; set { var sfilter = value?.ToString().TrimStart('^').TrimEnd('$') ?? string.Empty; if (sfilter == string.Empty || sfilter == "*") filter = null; else { if (value != null && value.ToString().TrimEnd('$').EndsWith("\\.job", StringComparison.InvariantCultureIgnoreCase)) filter = new Regex(value.ToString().Replace("\\.job", "")); else filter = value; } } } /// Gets the specified registered task from the collection. /// The index of the registered task to be retrieved. /// A instance that contains the requested context. public Task this[int index] { get { var i = 0; var te = GetEnumerator(); while (te.MoveNext()) if (i++ == index) return te.Current; throw new ArgumentOutOfRangeException(nameof(index)); } } /// Gets the named registered task from the collection. /// The name of the registered task to be retrieved. /// A instance that contains the requested context. public Task this[string name] { get { if (v2Coll != null) return Task.CreateTask(svc, v2Coll[name]); var v1Task = svc.GetTask(name); if (v1Task != null) return v1Task; throw new ArgumentOutOfRangeException(nameof(name)); } } /// Releases all resources used by this class. public void Dispose() { v1TS = null; if (v2Coll != null) Marshal.ReleaseComObject(v2Coll); } /// Determines whether the specified task exists. /// The name of the task. /// true if task exists; otherwise, false. public bool Exists([NotNull] string taskName) { try { if (v2Coll != null) return v2Coll[taskName] != null; return svc.GetTask(taskName) != null; } catch { } return false; } /// Gets the collection enumerator for the register task collection. /// An for this collection. public IEnumerator GetEnumerator() { if (v1TS != null) return new V1TaskEnumerator(svc, filter); return new V2TaskEnumerator(fld, v2Coll, filter); } /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() => $"TaskCollection; Count: {Count}"; System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); internal class V1TaskEnumerator : IEnumerator { private readonly Regex filter; private readonly TaskService svc; private readonly V1Interop.IEnumWorkItems wienum; private string curItem; private V1Interop.ITaskScheduler ts; /// Internal constructor /// TaskService instance /// The filter. internal V1TaskEnumerator(TaskService svc, Regex filter = null) { this.svc = svc; this.filter = filter; ts = svc.v1TaskScheduler; wienum = ts?.Enum(); Reset(); } /// Retrieves the current task. See for more information. public Task Current => new Task(svc, ICurrent); object System.Collections.IEnumerator.Current => Current; internal int Count { get { var i = 0; Reset(); while (MoveNext()) i++; Reset(); return i; } } internal V1Interop.ITask ICurrent => TaskService.GetTask(ts, curItem); /// Releases all resources used by this class. public void Dispose() { if (wienum != null) Marshal.ReleaseComObject(wienum); ts = null; } /// Moves to the next task. See MoveNext for more information. /// true if next task found, false if no more tasks. public bool MoveNext() { var names = IntPtr.Zero; var valid = false; do { curItem = null; uint uFetched = 0; try { wienum?.Next(1, out names, out uFetched); if (uFetched != 1) break; using (var name = new V1Interop.CoTaskMemString(Marshal.ReadIntPtr(names))) curItem = name.ToString(); if (curItem != null && curItem.EndsWith(".job", StringComparison.InvariantCultureIgnoreCase)) curItem = curItem.Remove(curItem.Length - 4); } catch { } finally { Marshal.FreeCoTaskMem(names); names = IntPtr.Zero; } // If name doesn't match filter, look for next item if (filter != null && curItem != null) { if (!filter.IsMatch(curItem)) continue; } V1Interop.ITask itask = null; try { itask = ICurrent; valid = true; } catch { valid = false; } finally { Marshal.ReleaseComObject(itask); } } while (!valid); return (curItem != null); } /// Reset task enumeration. See Reset for more information. public void Reset() { curItem = null; wienum?.Reset(); } } private class V2TaskEnumerator : ComEnumerator { private readonly Regex filter; internal V2TaskEnumerator(TaskFolder folder, V2Interop.IRegisteredTaskCollection iTaskColl, Regex filter = null) : base(() => iTaskColl.Count, (object o) => iTaskColl[o], o => Task.CreateTask(folder.TaskService, o)) => this.filter = filter; public override bool MoveNext() { var hasNext = base.MoveNext(); while (hasNext) { if (filter == null || filter.IsMatch(iEnum?.Current?.Name ?? "")) break; hasNext = base.MoveNext(); } return hasNext; } } } }