400 lines
12 KiB
C#
400 lines
12 KiB
C#
using JetBrains.Annotations;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace Microsoft.Win32.TaskScheduler
|
|
{
|
|
/// <summary>
|
|
/// Collection of running tasks in a <see cref="TaskService"/>. This class has no public constructor and can only be accessed via the
|
|
/// properties and functions within <see cref="TaskService"/>.
|
|
/// </summary>
|
|
public sealed class RunningTaskCollection : IReadOnlyList<RunningTask>, 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;
|
|
}
|
|
|
|
/// <summary>Gets the number of registered tasks in the collection.</summary>
|
|
public int Count
|
|
{
|
|
get
|
|
{
|
|
if (v2Coll != null)
|
|
return v2Coll.Count;
|
|
var i = 0;
|
|
var v1Enum = new V1RunningTaskEnumerator(svc);
|
|
while (v1Enum.MoveNext())
|
|
i++;
|
|
return i;
|
|
}
|
|
}
|
|
|
|
/// <summary>Gets the specified running task from the collection.</summary>
|
|
/// <param name="index">The index of the running task to be retrieved.</param>
|
|
/// <returns>A <see cref="RunningTask"/> instance.</returns>
|
|
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));
|
|
}
|
|
}
|
|
|
|
/// <summary>Releases all resources used by this class.</summary>
|
|
public void Dispose()
|
|
{
|
|
if (v2Coll != null)
|
|
Marshal.ReleaseComObject(v2Coll);
|
|
}
|
|
|
|
/// <summary>Gets an IEnumerator instance for this collection.</summary>
|
|
/// <returns>An enumerator.</returns>
|
|
public IEnumerator<RunningTask> GetEnumerator()
|
|
{
|
|
if (v2Coll != null)
|
|
return new ComEnumerator<RunningTask, V2Interop.IRunningTask>(() => 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);
|
|
}
|
|
|
|
/// <summary>Returns a <see cref="System.String"/> that represents this instance.</summary>
|
|
/// <returns>A <see cref="System.String"/> that represents this instance.</returns>
|
|
public override string ToString() => $"RunningTaskCollection; Count: {Count}";
|
|
|
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
|
|
|
|
private class V1RunningTaskEnumerator : IEnumerator<RunningTask>
|
|
{
|
|
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;
|
|
|
|
/// <summary>Releases all resources used by this class.</summary>
|
|
public void Dispose() => tEnum.Dispose();
|
|
|
|
public bool MoveNext() => tEnum.MoveNext() && (tEnum.Current?.State == TaskState.Running || MoveNext());
|
|
|
|
public void Reset() => tEnum.Reset();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains all the tasks that are registered within a <see cref="TaskFolder"/>. This class has no public constructor and can only be
|
|
/// accessed via the properties and functions within <see cref="TaskFolder"/>.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 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.
|
|
/// </remarks>
|
|
/// <example>
|
|
/// <code>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);
|
|
/// }
|
|
/// }</code>
|
|
/// </example>
|
|
[PublicAPI]
|
|
public sealed class TaskCollection : IReadOnlyList<Task>, 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;
|
|
}
|
|
|
|
/// <summary>Gets the number of registered tasks in the collection.</summary>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>Gets or sets the regular expression filter for task names.</summary>
|
|
/// <value>The regular expression filter.</value>
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Gets the specified registered task from the collection.</summary>
|
|
/// <param name="index">The index of the registered task to be retrieved.</param>
|
|
/// <returns>A <see cref="Task"/> instance that contains the requested context.</returns>
|
|
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));
|
|
}
|
|
}
|
|
|
|
/// <summary>Gets the named registered task from the collection.</summary>
|
|
/// <param name="name">The name of the registered task to be retrieved.</param>
|
|
/// <returns>A <see cref="Task"/> instance that contains the requested context.</returns>
|
|
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));
|
|
}
|
|
}
|
|
|
|
/// <summary>Releases all resources used by this class.</summary>
|
|
public void Dispose()
|
|
{
|
|
v1TS = null;
|
|
if (v2Coll != null)
|
|
Marshal.ReleaseComObject(v2Coll);
|
|
}
|
|
|
|
/// <summary>Determines whether the specified task exists.</summary>
|
|
/// <param name="taskName">The name of the task.</param>
|
|
/// <returns>true if task exists; otherwise, false.</returns>
|
|
public bool Exists([NotNull] string taskName)
|
|
{
|
|
try
|
|
{
|
|
if (v2Coll != null)
|
|
return v2Coll[taskName] != null;
|
|
|
|
return svc.GetTask(taskName) != null;
|
|
}
|
|
catch { }
|
|
return false;
|
|
}
|
|
|
|
/// <summary>Gets the collection enumerator for the register task collection.</summary>
|
|
/// <returns>An <see cref="System.Collections.IEnumerator"/> for this collection.</returns>
|
|
public IEnumerator<Task> GetEnumerator()
|
|
{
|
|
if (v1TS != null)
|
|
return new V1TaskEnumerator(svc, filter);
|
|
return new V2TaskEnumerator(fld, v2Coll, filter);
|
|
}
|
|
|
|
/// <summary>Returns a <see cref="System.String"/> that represents this instance.</summary>
|
|
/// <returns>A <see cref="System.String"/> that represents this instance.</returns>
|
|
public override string ToString() => $"TaskCollection; Count: {Count}";
|
|
|
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
|
|
|
|
internal class V1TaskEnumerator : IEnumerator<Task>
|
|
{
|
|
private readonly Regex filter;
|
|
private readonly TaskService svc;
|
|
private readonly V1Interop.IEnumWorkItems wienum;
|
|
private string curItem;
|
|
private V1Interop.ITaskScheduler ts;
|
|
|
|
/// <summary>Internal constructor</summary>
|
|
/// <param name="svc">TaskService instance</param>
|
|
/// <param name="filter">The filter.</param>
|
|
internal V1TaskEnumerator(TaskService svc, Regex filter = null)
|
|
{
|
|
this.svc = svc;
|
|
this.filter = filter;
|
|
ts = svc.v1TaskScheduler;
|
|
wienum = ts?.Enum();
|
|
Reset();
|
|
}
|
|
|
|
/// <summary>Retrieves the current task. See <see cref="System.Collections.IEnumerator.Current"/> for more information.</summary>
|
|
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);
|
|
|
|
/// <summary>Releases all resources used by this class.</summary>
|
|
public void Dispose()
|
|
{
|
|
if (wienum != null) Marshal.ReleaseComObject(wienum);
|
|
ts = null;
|
|
}
|
|
|
|
/// <summary>Moves to the next task. See MoveNext for more information.</summary>
|
|
/// <returns>true if next task found, false if no more tasks.</returns>
|
|
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);
|
|
}
|
|
|
|
/// <summary>Reset task enumeration. See Reset for more information.</summary>
|
|
public void Reset()
|
|
{
|
|
curItem = null;
|
|
wienum?.Reset();
|
|
}
|
|
}
|
|
|
|
private class V2TaskEnumerator : ComEnumerator<Task, V2Interop.IRegisteredTask>
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
} |