using System; using System.Collections; using System.Collections.Generic; using System.Xml.Serialization; namespace Microsoft.Win32.TaskScheduler { /// /// Collection that contains the actions that are performed by the task. /// /// A Task Scheduler 1.0 task can only contain a single . [XmlRoot("Actions", Namespace = TaskDefinition.tns, IsNullable = false)] public sealed class ActionCollection : IList, IDisposable, IXmlSerializable { private V1Interop.ITask v1Task; private V2Interop.ITaskDefinition v2Def; private V2Interop.IActionCollection v2Coll; internal ActionCollection(V1Interop.ITask task) { v1Task = task; } internal ActionCollection(V2Interop.ITaskDefinition iTaskDef) { v2Def = iTaskDef; v2Coll = iTaskDef.Actions; if (TaskService.LibraryVersion.Minor > 3) UnconvertUnsupportedActions(); } /// /// Releases all resources used by this class. /// public void Dispose() { v1Task = null; v2Def = null; v2Coll = null; } /// /// Adds an action to the task. /// /// A derived class. /// The bound that was added to the collection. public Action Add(Action action) { if (v2Def != null) action.Bind(v2Def); else action.Bind(v1Task); return action; } /// /// Adds an to the task. /// /// Path to an executable file. /// Arguments associated with the command-line operation. This value can be null. /// Directory that contains either the executable file or the files that are used by the executable file. This value can be null. /// The bound that was added to the collection. public ExecAction Add(string path, string arguments = null, string workingDirectory = null) { return (ExecAction)this.Add(new ExecAction(path, arguments, workingDirectory)); } /// /// Adds a new instance to the task. /// /// Type of task to be created /// Specialized instance. public Action AddNew(TaskActionType actionType) { if (v1Task != null) return new ExecAction(v1Task); return Action.CreateAction(v2Coll.Create(actionType)); } /// /// Clears all actions from the task. /// public void Clear() { if (v2Coll != null) v2Coll.Clear(); else Add(new ExecAction()); } /// /// Determines whether the contains a specific value. /// /// The object to locate in the . /// /// true if is found in the ; otherwise, false. /// public bool Contains(Action item) { return IndexOf(item) >= 0; } /// /// Determines whether the specified action type is contained in this collection. /// /// Type of the action. /// /// true if the specified action type is contained in this collection; otherwise, false. /// public bool ContainsType(Type actionType) { foreach (Action a in this) if (a.GetType() == actionType) return true; return false; } /// /// Copies the elements of the to an , starting at a particular index. /// /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. /// The zero-based index in at which copying begins. /// is null. /// is less than 0. /// The number of elements in the source is greater than the available space from to the end of the destination . public void CopyTo(Action[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException(); if (arrayIndex < 0) throw new ArgumentOutOfRangeException(); if (this.Count > (array.Length - arrayIndex)) throw new ArgumentException(); for (int i = 0; i < this.Count; i++) array[arrayIndex + i] = (Action)this[i].Clone(); } private const string ScriptIdentifer = "TSML_20140424"; internal void ConvertUnsupportedActions() { #if DEBUG const string PowerShellArgFormat = "-NoExit -Command \"& {{<# {0}:{1} #> {2}}}\""; #else const string PowerShellArgFormat = "-NoLogo -NonInteractive -WindowStyle Hidden -Command \"& {{<# {0}:{1} #> {2}}}\""; #endif const string PowerShellPath = "powershell"; for (int i = 0; i < this.Count; i++) { Action action = this[i]; if (action is IBindAsExecAction) { string args = string.Format(PowerShellArgFormat, ScriptIdentifer, action.ActionType, ((IBindAsExecAction)action).GetPowerShellCommand()); ExecAction newAction = new ExecAction(PowerShellPath, args); this[i] = newAction; } } } internal void UnconvertUnsupportedActions() { for (int i = 0; i < this.Count; i++) { ExecAction action = this[i] as ExecAction; if (action != null && action.Arguments != null && action.Arguments.Contains(ScriptIdentifer)) { var match = System.Text.RegularExpressions.Regex.Match(action.Arguments, @"<# " + ScriptIdentifer + ":(?\\w+) #> (?.+)}\"$"); if (match.Success) { Action newAction = null; if (match.Groups["type"].Value == "SendEmail") newAction = EmailAction.FromPowerShellCommand(match.Groups["cmd"].Value); else if (match.Groups["type"].Value == "ShowMessage") newAction = ShowMessageAction.FromPowerShellCommand(match.Groups["cmd"].Value); if (newAction != null) this[i] = newAction; } } } } /// /// Determines the index of a specific item in the . /// /// The object to locate in the . /// /// The index of if found in the list; otherwise, -1. /// public int IndexOf(Action item) { for (int i = 0; i < this.Count; i++) { if (this[i].Equals(item)) return i; } return -1; } /// /// Determines the index of a specific item in the . /// /// The id () of the action to be retrieved. /// /// The index of if found in the list; otherwise, -1. /// public int IndexOf(string actionId) { if (string.IsNullOrEmpty(actionId)) throw new ArgumentNullException(actionId); for (int i = 0; i < this.Count; i++) { if (string.Equals(this[i].Id, actionId)) return i; } return -1; } /// /// Inserts an action at the specified index. /// /// The zero-based index at which action should be inserted. /// The action to insert into the list. public void Insert(int index, Action action) { if (v2Coll == null && this.Count > 0) throw new NotV1SupportedException("Only a single action is allowed."); Action[] pushItems = new Action[this.Count - index]; for (int i = index; i < this.Count; i++) pushItems[i - index] = (Action)this[i].Clone(); for (int j = this.Count - 1; j >= index; j--) RemoveAt(j); Add(action); for (int k = 0; k < pushItems.Length; k++) Add(pushItems[k]); } /// /// Removes the first occurrence of a specific object from the . /// /// The object to remove from the . /// /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . /// public bool Remove(Action item) { int idx = IndexOf(item); if (idx != -1) { try { RemoveAt(idx); return true; } catch { } } return false; } /// /// Removes the action at a specified index. /// /// Index of action to remove. /// Index out of range. public void RemoveAt(int index) { if (index >= this.Count) throw new ArgumentOutOfRangeException("index", index, "Failed to remove action. Index out of range."); if (v2Coll != null) v2Coll.Remove(++index); else if (index == 0) Add(new ExecAction()); else throw new NotV1SupportedException("There can be only a single action and it cannot be removed."); } /// /// Returns a that represents the actions in this collection. /// /// /// A that represents the actions in this collection. /// public override string ToString() { if (this.Count == 1) return this[0].ToString(); if (this.Count > 1) return Properties.Resources.MultipleActions; return string.Empty; } /// /// Gets or sets a an action at the specified index. /// /// The zero-based index of the action to get or set. public Action this[int index] { get { if (v2Coll != null) return Action.CreateAction(v2Coll[++index]); if (index == 0) return new ExecAction(v1Task.GetApplicationName(), v1Task.GetParameters(), v1Task.GetWorkingDirectory()); throw new ArgumentOutOfRangeException(); } set { if (this.Count <= index) throw new ArgumentOutOfRangeException("index", index, "Index is not a valid index in the ActionCollection"); RemoveAt(index); Insert(index, value); } } /// /// Gets or sets a specified action from the collection. /// /// /// The . /// /// The id () of the action to be retrieved. /// /// Specialized instance. /// /// /// /// /// Mismatching Id for action and lookup. public Action this[string actionId] { get { if (string.IsNullOrEmpty(actionId)) throw new ArgumentNullException(actionId); foreach (Action t in this) if (string.Equals(t.Id, actionId)) return t; throw new ArgumentOutOfRangeException(actionId); } set { if (value == null) throw new NullReferenceException(); if (string.IsNullOrEmpty(actionId)) throw new ArgumentNullException(actionId); if (actionId != value.Id) throw new InvalidOperationException("Mismatching Id for action and lookup."); int index = IndexOf(actionId); if (index >= 0) { RemoveAt(index); Insert(index, value); } else Add(value); } } /// /// Gets or sets the identifier of the principal for the task. /// /// Not supported under Task Scheduler 1.0. [System.Xml.Serialization.XmlAttribute(AttributeName = "Context", DataType = "IDREF")] public string Context { get { if (v2Coll != null) return v2Coll.Context; return string.Empty; } set { if (v2Coll != null) v2Coll.Context = value; else throw new NotV1SupportedException(); } } /// /// Gets the number of actions in the collection. /// public int Count { get { if (v2Coll != null) return v2Coll.Count; return ((string)v1Task.GetApplicationName()).Length == 0 ? 0 : 1; } } /// /// Gets or sets an XML-formatted version of the collection. /// public string XmlText { get { if (v2Coll != null) return v2Coll.XmlText; return XmlSerializationHelper.WriteObjectToXmlText(this); } set { if (v2Coll != null) v2Coll.XmlText = value; else XmlSerializationHelper.ReadObjectFromXmlText(value, this); } } /// /// Retrieves an enumeration of each of the actions. /// /// Returns an object that implements the interface and that can iterate through the objects within the . public IEnumerator GetEnumerator() { if (v2Coll != null) return new Enumerator(this); return new Enumerator(this.v1Task); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.GetEnumerator(); } internal class Enumerator : IEnumerator { private V1Interop.ITask v1Task; private int v1Pos = -1; private IEnumerator v2Enum; private ActionCollection parent; internal Enumerator(V1Interop.ITask task) { v1Task = task; } internal Enumerator(ActionCollection iColl) { parent = iColl; if (iColl.v2Coll != null) v2Enum = iColl.v2Coll.GetEnumerator(); } public Action Current { get { if (v2Enum != null) { V2Interop.IAction iAction = v2Enum.Current as V2Interop.IAction; if (iAction != null) return Action.CreateAction(iAction); } if (v1Pos == 0) return new ExecAction(v1Task.GetApplicationName(), v1Task.GetParameters(), v1Task.GetWorkingDirectory()); throw new InvalidOperationException(); } } /// /// Releases all resources used by this class. /// public void Dispose() { v1Task = null; v2Enum = null; } object System.Collections.IEnumerator.Current { get { return this.Current; } } public bool MoveNext() { if (v2Enum != null) return v2Enum.MoveNext(); return ++v1Pos == 0; } public void Reset() { if (v2Enum != null) v2Enum.Reset(); v1Pos = -1; } } System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() { return null; } void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { reader.ReadStartElement("Actions", TaskDefinition.tns); while (reader.MoveToContent() == System.Xml.XmlNodeType.Element) { Action newAction = null; switch (reader.LocalName) { case "Exec": newAction = this.AddNew(TaskActionType.Execute); break; default: reader.Skip(); break; } if (newAction != null) XmlSerializationHelper.ReadObject(reader, newAction); } reader.ReadEndElement(); } void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { if (this.Count > 0) { XmlSerializationHelper.WriteObject(writer, this[0] as ExecAction); } } void ICollection.Add(Action item) { this.Add(item); } bool ICollection.IsReadOnly { get { return false; } } } }