using JetBrains.Annotations; using Microsoft.Win32.TaskScheduler.V2Interop; using System; using System.Collections.Generic; using System.ComponentModel; using System.Reflection; using System.Runtime.InteropServices; using System.Xml.Serialization; namespace Microsoft.Win32.TaskScheduler { /// Defines the type of actions a task can perform. /// The action type is defined when the action is created and cannot be changed later. See . public enum TaskActionType { /// /// This action performs a command-line operation. For example, the action can run a script, launch an executable, or, if the name /// of a document is provided, find its associated application and launch the application with the document. /// Execute = 0, /// This action fires a handler. ComHandler = 5, /// This action sends and e-mail. SendEmail = 6, /// This action shows a message box. ShowMessage = 7 } /// An interface that exposes the ability to convert an actions functionality to a PowerShell script. internal interface IBindAsExecAction { } /// /// Abstract base class that provides the common properties that are inherited by all action objects. An action object is created by the /// method. /// [PublicAPI] public abstract class Action : IDisposable, ICloneable, IEquatable, INotifyPropertyChanged, IComparable, IComparable { internal IAction iAction; internal V1Interop.ITask v1Task; /// List of unbound values when working with Actions not associated with a registered task. protected readonly Dictionary unboundValues = new Dictionary(); internal Action() { } internal Action([NotNull] IAction action) => iAction = action; internal Action([NotNull] V1Interop.ITask iTask) => v1Task = iTask; /// Occurs when a property value changes. public event PropertyChangedEventHandler PropertyChanged; /// Gets the type of the action. /// The type of the action. [XmlIgnore] public TaskActionType ActionType => iAction?.Type ?? InternalActionType; /// Gets or sets the identifier of the action. [DefaultValue(null)] [XmlAttribute(AttributeName = "id")] public virtual string Id { get => GetProperty(nameof(Id)); set => SetProperty(nameof(Id), value); } internal abstract TaskActionType InternalActionType { get; } /// Creates the specified action. /// Type of the action to instantiate. /// of specified type. public static Action CreateAction(TaskActionType actionType) => Activator.CreateInstance(GetObjectType(actionType)) as Action; /// Creates a new object that is a copy of the current instance. /// A new object that is a copy of this instance. public object Clone() { var ret = CreateAction(ActionType); ret.CopyProperties(this); return ret; } /// /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current /// instance precedes, follows, or occurs in the same position in the sort order as the other object. /// /// An object to compare with this instance. /// A value that indicates the relative order of the objects being compared. public int CompareTo(Action obj) => string.Compare(Id, obj?.Id, StringComparison.InvariantCulture); /// Releases all resources used by this class. public virtual void Dispose() { if (iAction != null) Marshal.ReleaseComObject(iAction); } /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. public override bool Equals([CanBeNull] object obj) { if (obj is Action) return Equals((Action)obj); return base.Equals(obj); } /// Indicates whether the current object is equal to another object of the same type. /// An object to compare with this object. /// true if the current object is equal to the parameter; otherwise, false. public virtual bool Equals([NotNull] Action other) => ActionType == other.ActionType && Id == other.Id; /// Returns a hash code for this instance. /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. public override int GetHashCode() => new { A = ActionType, B = Id }.GetHashCode(); /// Returns the action Id. /// String representation of action. public override string ToString() => Id; /// Returns a that represents this action. /// The culture. /// String representation of action. public virtual string ToString([NotNull] System.Globalization.CultureInfo culture) { using (new CultureSwitcher(culture)) return ToString(); } int IComparable.CompareTo(object obj) => CompareTo(obj as Action); internal static Action ActionFromScript(string actionType, string script) { var tat = TryParse(actionType, TaskActionType.Execute); var t = GetObjectType(tat); return (Action)t.InvokeMember("FromPowerShellCommand", BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod, null, null, new object[] { script }); } internal static Action ConvertFromPowerShellAction(ExecAction execAction) { var psi = execAction.ParsePowerShellItems(); if (psi != null && psi.Length == 2) { var a = ActionFromScript(psi[0], psi[1]); if (a != null) { a.v1Task = execAction.v1Task; a.iAction = execAction.iAction; return a; } } return null; } /// Creates a specialized class from a defined interface. /// Version 1.0 interface. /// Specialized action class internal static Action CreateAction(V1Interop.ITask iTask) { var tempAction = new ExecAction(iTask); return ConvertFromPowerShellAction(tempAction) ?? tempAction; } /// Creates a specialized class from a defined interface. /// Version 2.0 Action interface. /// Specialized action class internal static Action CreateAction(IAction iAction) { var t = GetObjectType(iAction.Type); return Activator.CreateInstance(t, BindingFlags.CreateInstance | BindingFlags.NonPublic | BindingFlags.Instance, null, new object[] { iAction }, null) as Action; } internal static T TryParse(string val, T defaultVal) { var ret = defaultVal; if (val != null) try { ret = (T)Enum.Parse(typeof(T), val); } catch { } return ret; } internal virtual void Bind(V1Interop.ITask iTask) { if (Id != null) iTask.SetDataItem("ActionId", Id); var bindable = this as IBindAsExecAction; if (bindable != null) iTask.SetDataItem("ActionType", InternalActionType.ToString()); unboundValues.TryGetValue("Path", out var o); iTask.SetApplicationName(bindable != null ? ExecAction.PowerShellPath : o?.ToString() ?? string.Empty); unboundValues.TryGetValue("Arguments", out o); iTask.SetParameters(bindable != null ? ExecAction.BuildPowerShellCmd(ActionType.ToString(), GetPowerShellCommand()) : o?.ToString() ?? string.Empty); unboundValues.TryGetValue("WorkingDirectory", out o); iTask.SetWorkingDirectory(o?.ToString() ?? string.Empty); } internal virtual void Bind(ITaskDefinition iTaskDef) { var iActions = iTaskDef.Actions; if (iActions.Count >= ActionCollection.MaxActions) throw new ArgumentOutOfRangeException(nameof(iTaskDef), @"A maximum of 32 actions is allowed within a single task."); CreateV2Action(iActions); Marshal.ReleaseComObject(iActions); foreach (var key in unboundValues.Keys) { try { ReflectionHelper.SetProperty(iAction, key, unboundValues[key]); } catch (TargetInvocationException tie) { throw tie.InnerException; } catch { } } unboundValues.Clear(); } /// Copies the properties from another the current instance. /// The source . internal virtual void CopyProperties([NotNull] Action sourceAction) => Id = sourceAction.Id; internal abstract void CreateV2Action(IActionCollection iActions); internal abstract string GetPowerShellCommand(); internal T GetProperty(string propName, T defaultValue = default) { if (iAction == null) return unboundValues.TryGetValue(propName, out var value) ? (T)value : defaultValue; return ReflectionHelper.GetProperty((TB)iAction, propName, defaultValue); } internal void OnPropertyChanged(string propName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); internal void SetProperty(string propName, T value) { if (iAction == null) { if (Equals(value, default(T))) unboundValues.Remove(propName); else unboundValues[propName] = value; } else ReflectionHelper.SetProperty((TB)iAction, propName, value); OnPropertyChanged(propName); } [NotNull] private static Type GetObjectType(TaskActionType actionType) => actionType switch { TaskActionType.ComHandler => typeof(ComHandlerAction), TaskActionType.SendEmail => typeof(EmailAction), TaskActionType.ShowMessage => typeof(ShowMessageAction), _ => typeof(ExecAction), }; } /// /// Represents an action that fires a handler. Only available on Task Scheduler 2.0. Only available for Task Scheduler 2.0 on /// Windows Vista or Windows Server 2003 and later. /// /// /// This action is the most complex. It allows the task to execute and In-Proc COM server object that implements the ITaskHandler /// interface. There is a sample project that shows how to do this in the Downloads section. /// /// /// /// /// /// [XmlType(IncludeInSchema = true)] [XmlRoot("ComHandler", Namespace = TaskDefinition.tns, IsNullable = false)] public class ComHandlerAction : Action, IBindAsExecAction { /// Creates an unbound instance of . public ComHandlerAction() { } /// Creates an unbound instance of . /// Identifier of the handler class. /// Addition data associated with the handler. public ComHandlerAction(Guid classId, [CanBeNull] string data) { ClassId = classId; Data = data; } internal ComHandlerAction([NotNull] V1Interop.ITask task) : base(task) { } internal ComHandlerAction([NotNull] IAction action) : base(action) { } /// Gets or sets the identifier of the handler class. public Guid ClassId { get => new Guid(GetProperty(nameof(ClassId), Guid.Empty.ToString())); set => SetProperty(nameof(ClassId), value.ToString()); } /// Gets the name of the object referred to by . public string ClassName => GetNameForCLSID(ClassId); /// Gets or sets additional data that is associated with the handler. [DefaultValue(null)] [CanBeNull] public string Data { get => GetProperty(nameof(Data)); set => SetProperty(nameof(Data), value); } internal override TaskActionType InternalActionType => TaskActionType.ComHandler; /// Indicates whether the current object is equal to another object of the same type. /// An object to compare with this object. /// true if the current object is equal to the parameter; otherwise, false. public override bool Equals(Action other) => base.Equals(other) && ClassId == ((ComHandlerAction)other).ClassId && Data == ((ComHandlerAction)other).Data; /// Gets a string representation of the . /// String representation of this action. public override string ToString() => string.Format(Properties.Resources.ComHandlerAction, ClassId, Data, Id, ClassName); internal static Action FromPowerShellCommand(string p) { var match = System.Text.RegularExpressions.Regex.Match(p, @"^\[Reflection.Assembly\]::LoadFile\('(?:[^']*)'\); \[Microsoft.Win32.TaskScheduler.TaskService\]::RunComHandlerAction\(\[GUID\]\('(?[^']*)'\), '(?[^']*)'\);?\s*$"); return match.Success ? new ComHandlerAction(new Guid(match.Groups["g"].Value), match.Groups["d"].Value.Replace("''", "'")) : null; } /// Copies the properties from another the current instance. /// The source . internal override void CopyProperties(Action sourceAction) { if (sourceAction.GetType() == GetType()) { base.CopyProperties(sourceAction); ClassId = ((ComHandlerAction)sourceAction).ClassId; Data = ((ComHandlerAction)sourceAction).Data; } } internal override void CreateV2Action([NotNull] IActionCollection iActions) => iAction = iActions.Create(TaskActionType.ComHandler); internal override string GetPowerShellCommand() { var sb = new System.Text.StringBuilder(); sb.Append($"[Reflection.Assembly]::LoadFile('{Assembly.GetExecutingAssembly().Location}'); "); sb.Append($"[Microsoft.Win32.TaskScheduler.TaskService]::RunComHandlerAction([GUID]('{ClassId:D}'), '{Data?.Replace("'", "''") ?? string.Empty}'); "); return sb.ToString(); } /// Gets the name for CLSID. /// The unique identifier. /// [CanBeNull] private static string GetNameForCLSID(Guid guid) { using (var k = Registry.ClassesRoot.OpenSubKey("CLSID", false)) { if (k != null) { using var k2 = k.OpenSubKey(guid.ToString("B"), false); return k2?.GetValue(null) as string; } } return null; } } /// /// Represents an action that sends an e-mail. Only available for Task Scheduler 2.0 on Windows Vista or Windows Server 2003 and /// later.This action has been deprecated in Windows 8 and later. However, this library is able to mimic its /// functionality using PowerShell if the property is set to . To disable this conversion, set the value to . /// /// The EmailAction allows for an email to be sent when the task is triggered. /// /// /// /// /// [XmlType(IncludeInSchema = true)] [XmlRoot("SendEmail", Namespace = TaskDefinition.tns, IsNullable = false)] public sealed class EmailAction : Action, IBindAsExecAction { private const string ImportanceHeader = "Importance"; private NamedValueCollection nvc; private bool validateAttachments = true; /// Creates an unbound instance of . public EmailAction() { } /// Creates an unbound instance of . /// Subject of the e-mail. /// E-mail address that you want to send the e-mail from. /// E-mail address or addresses that you want to send the e-mail to. /// Body of the e-mail that contains the e-mail message. /// Name of the server that you use to send e-mail from. public EmailAction([CanBeNull] string subject, [NotNull] string from, [NotNull] string to, [CanBeNull] string body, [NotNull] string mailServer) { Subject = subject; From = from; To = to; Body = body; Server = mailServer; } internal EmailAction([NotNull] V1Interop.ITask task) : base(task) { } internal EmailAction([NotNull] IAction action) : base(action) { } /// /// Gets or sets an array of file paths to be sent as attachments with the e-mail. Each item must be a value /// containing a path to file. /// [XmlArray("Attachments", IsNullable = true)] [XmlArrayItem("File", typeof(string))] [DefaultValue(null)] public object[] Attachments { get => GetProperty(nameof(Attachments)); set { if (value != null) { if (value.Length > 8) throw new ArgumentOutOfRangeException(nameof(Attachments), @"Attachments array cannot contain more than 8 items."); if (validateAttachments) { foreach (var o in value) if (!(o is string) || !System.IO.File.Exists((string)o)) throw new ArgumentException(@"Each value of the array must contain a valid file reference.", nameof(Attachments)); } } if (iAction == null && (value == null || value.Length == 0)) { unboundValues.Remove(nameof(Attachments)); OnPropertyChanged(nameof(Attachments)); } else SetProperty(nameof(Attachments), value); } } /// Gets or sets the e-mail address or addresses that you want to Bcc in the e-mail. [DefaultValue(null)] public string Bcc { get => GetProperty(nameof(Bcc)); set => SetProperty(nameof(Bcc), value); } /// Gets or sets the body of the e-mail that contains the e-mail message. [DefaultValue(null)] public string Body { get => GetProperty(nameof(Body)); set => SetProperty(nameof(Body), value); } /// Gets or sets the e-mail address or addresses that you want to Cc in the e-mail. [DefaultValue(null)] public string Cc { get => GetProperty(nameof(Cc)); set => SetProperty(nameof(Cc), value); } /// Gets or sets the e-mail address that you want to send the e-mail from. [DefaultValue(null)] public string From { get => GetProperty(nameof(From)); set => SetProperty(nameof(From), value); } /// Gets or sets the header information in the e-mail message to send. [XmlArray] [XmlArrayItem("HeaderField", typeof(NameValuePair))] [NotNull] public NamedValueCollection HeaderFields { get { if (nvc == null) { nvc = iAction == null ? new NamedValueCollection() : new NamedValueCollection(((IEmailAction)iAction).HeaderFields); nvc.AttributedXmlFormat = false; nvc.CollectionChanged += (o, e) => OnPropertyChanged(nameof(HeaderFields)); } return nvc; } } /// Gets or sets the priority of the e-mail message. /// A that contains the priority of this message. [XmlIgnore] [DefaultValue(typeof(System.Net.Mail.MailPriority), "Normal")] public System.Net.Mail.MailPriority Priority { get { if (nvc != null && HeaderFields.TryGetValue(ImportanceHeader, out var s)) return TryParse(s, System.Net.Mail.MailPriority.Normal); return System.Net.Mail.MailPriority.Normal; } set => HeaderFields[ImportanceHeader] = value.ToString(); } /// Gets or sets the e-mail address that you want to reply to. [DefaultValue(null)] public string ReplyTo { get => GetProperty(nameof(ReplyTo)); set => SetProperty(nameof(ReplyTo), value); } /// Gets or sets the name of the server that you use to send e-mail from. [DefaultValue(null)] public string Server { get => GetProperty(nameof(Server)); set => SetProperty(nameof(Server), value); } /// Gets or sets the subject of the e-mail. [DefaultValue(null)] public string Subject { get => GetProperty(nameof(Subject)); set => SetProperty(nameof(Subject), value); } /// Gets or sets the e-mail address or addresses that you want to send the e-mail to. [DefaultValue(null)] public string To { get => GetProperty(nameof(To)); set => SetProperty(nameof(To), value); } internal override TaskActionType InternalActionType => TaskActionType.SendEmail; /// Indicates whether the current object is equal to another object of the same type. /// An object to compare with this object. /// true if the current object is equal to the parameter; otherwise, false. public override bool Equals(Action other) => base.Equals(other) && GetPowerShellCommand() == other.GetPowerShellCommand(); /// Gets a string representation of the . /// String representation of this action. public override string ToString() => string.Format(Properties.Resources.EmailAction, Subject, To, Cc, Bcc, From, ReplyTo, Body, Server, Id); internal static Action FromPowerShellCommand(string p) { var match = System.Text.RegularExpressions.Regex.Match(p, @"^Send-MailMessage -From '(?(?:[^']|'')*)' -Subject '(?(?:[^']|'')*)' -SmtpServer '(?(?:[^']|'')*)'(?: -Encoding UTF8)?(?: -To (?'(?:(?:[^']|'')*)'(?:, '(?:(?:[^']|'')*)')*))?(?: -Cc (?'(?:(?:[^']|'')*)'(?:, '(?:(?:[^']|'')*)')*))?(?: -Bcc (?'(?:(?:[^']|'')*)'(?:, '(?:(?:[^']|'')*)')*))?(?:(?: -BodyAsHtml)? -Body '(?(?:[^']|'')*)')?(?: -Attachments (?'(?:(?:[^']|'')*)'(?:, '(?:(?:[^']|'')*)')*))?(?: -Priority (?High|Normal|Low))?;?\s*$"); if (match.Success) { var action = new EmailAction(UnPrep(FromUTF8(match.Groups["subject"].Value)), UnPrep(match.Groups["from"].Value), FromPS(match.Groups["to"]), UnPrep(FromUTF8(match.Groups["body"].Value)), UnPrep(match.Groups["server"].Value)) { Cc = FromPS(match.Groups["cc"]), Bcc = FromPS(match.Groups["bcc"]) }; action.validateAttachments = false; if (match.Groups["att"].Success) action.Attachments = Array.ConvertAll(FromPS(match.Groups["att"].Value), s => s); action.validateAttachments = true; if (match.Groups["imp"].Success) action.HeaderFields[ImportanceHeader] = match.Groups["imp"].Value; return action; } return null; } internal override void Bind(ITaskDefinition iTaskDef) { base.Bind(iTaskDef); nvc?.Bind(((IEmailAction)iAction).HeaderFields); } /// Copies the properties from another the current instance. /// The source . internal override void CopyProperties(Action sourceAction) { if (sourceAction.GetType() == GetType()) { base.CopyProperties(sourceAction); if (((EmailAction)sourceAction).Attachments != null) Attachments = (object[])((EmailAction)sourceAction).Attachments.Clone(); Bcc = ((EmailAction)sourceAction).Bcc; Body = ((EmailAction)sourceAction).Body; Cc = ((EmailAction)sourceAction).Cc; From = ((EmailAction)sourceAction).From; if (((EmailAction)sourceAction).nvc != null) ((EmailAction)sourceAction).HeaderFields.CopyTo(HeaderFields); ReplyTo = ((EmailAction)sourceAction).ReplyTo; Server = ((EmailAction)sourceAction).Server; Subject = ((EmailAction)sourceAction).Subject; To = ((EmailAction)sourceAction).To; } } internal override void CreateV2Action(IActionCollection iActions) => iAction = iActions.Create(TaskActionType.SendEmail); internal override string GetPowerShellCommand() { // Send-MailMessage [-To] [-Subject] [[-Body] ] [[-SmtpServer] ] -From // [-Attachments ] [-Bcc ] [-BodyAsHtml] [-Cc ] [-Credential ] // [-DeliveryNotificationOption ] [-Encoding ] [-Port ] [-Priority // ] [-UseSsl] [ ] var bodyIsHtml = Body != null && Body.Trim().StartsWith("<") && Body.Trim().EndsWith(">"); var sb = new System.Text.StringBuilder(); sb.AppendFormat("Send-MailMessage -From '{0}' -Subject '{1}' -SmtpServer '{2}' -Encoding UTF8", Prep(From), ToUTF8(Prep(Subject)), Prep(Server)); if (!string.IsNullOrEmpty(To)) sb.AppendFormat(" -To {0}", ToPS(To)); if (!string.IsNullOrEmpty(Cc)) sb.AppendFormat(" -Cc {0}", ToPS(Cc)); if (!string.IsNullOrEmpty(Bcc)) sb.AppendFormat(" -Bcc {0}", ToPS(Bcc)); if (bodyIsHtml) sb.Append(" -BodyAsHtml"); if (!string.IsNullOrEmpty(Body)) sb.AppendFormat(" -Body '{0}'", ToUTF8(Prep(Body))); if (Attachments != null && Attachments.Length > 0) sb.AppendFormat(" -Attachments {0}", ToPS(Array.ConvertAll(Attachments, o => Prep(o.ToString())))); var hdr = new List(HeaderFields.Names); if (hdr.Contains(ImportanceHeader)) { var p = Priority; if (p != System.Net.Mail.MailPriority.Normal) sb.Append($" -Priority {p}"); hdr.Remove(ImportanceHeader); } if (hdr.Count > 0) throw new InvalidOperationException("Under Windows 8 and later, EmailAction objects are converted to PowerShell. This action contains headers that are not supported."); sb.Append("; "); return sb.ToString(); /*var msg = new System.Net.Mail.MailMessage(this.From, this.To, this.Subject, this.Body); if (!string.IsNullOrEmpty(this.Bcc)) msg.Bcc.Add(this.Bcc); if (!string.IsNullOrEmpty(this.Cc)) msg.CC.Add(this.Cc); if (!string.IsNullOrEmpty(this.ReplyTo)) msg.ReplyTo = new System.Net.Mail.MailAddress(this.ReplyTo); if (this.Attachments != null && this.Attachments.Length > 0) foreach (string s in this.Attachments) msg.Attachments.Add(new System.Net.Mail.Attachment(s)); if (this.nvc != null) foreach (var ha in this.HeaderFields) msg.Headers.Add(ha.Name, ha.Value); var client = new System.Net.Mail.SmtpClient(this.Server); client.Send(msg);*/ } private static string[] FromPS(string p) { var list = p.Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries); return Array.ConvertAll(list, i => UnPrep(i).Trim('\'')); } private static string FromPS(System.Text.RegularExpressions.Group g, string delimeter = ";") => g.Success ? string.Join(delimeter, FromPS(g.Value)) : null; private static string FromUTF8(string s) { var bytes = System.Text.Encoding.UTF8.GetBytes(s); return System.Text.Encoding.Default.GetString(bytes); } private static string Prep(string s) => s?.Replace("'", "''"); private static string ToPS(string input, char[] delimeters = null) { if (delimeters == null) delimeters = new[] { ';', ',' }; return ToPS(Array.ConvertAll(input.Split(delimeters), i => Prep(i.Trim()))); } private static string ToPS(string[] input) => string.Join(", ", Array.ConvertAll(input, i => string.Concat("'", i.Trim(), "'"))); private static string ToUTF8(string s) { if (s == null) return null; var bytes = System.Text.Encoding.Default.GetBytes(s); return System.Text.Encoding.UTF8.GetString(bytes); } private static string UnPrep(string s) => s?.Replace("''", "'"); } /// Represents an action that executes a command-line operation. /// /// All versions of the base library support the ExecAction. It only has three properties that allow it to run an executable with parameters. /// /// /// /// /// /// [XmlRoot("Exec", Namespace = TaskDefinition.tns, IsNullable = false)] public class ExecAction : Action { #if DEBUG internal const string PowerShellArgFormat = "-NoExit -Command \"& {{<# {0}:{1} #> {2}}}\""; #else internal const string PowerShellArgFormat = "-NoLogo -NonInteractive -WindowStyle Hidden -Command \"& {{<# {0}:{1} #> {2}}}\""; #endif internal const string PowerShellPath = "powershell"; internal const string ScriptIdentifer = "TSML_20140424"; /// Creates a new instance of an that can be added to . public ExecAction() { } /// Creates a new instance of an that can be added to . /// 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. /// public ExecAction([NotNull] string path, string arguments = null, string workingDirectory = null) { Path = path; Arguments = arguments; WorkingDirectory = workingDirectory; } internal ExecAction([NotNull] V1Interop.ITask task) : base(task) { } internal ExecAction([NotNull] IAction action) : base(action) { } /// Gets or sets the arguments associated with the command-line operation. [DefaultValue("")] public string Arguments { get { if (v1Task != null) return v1Task.GetParameters(); return GetProperty(nameof(Arguments), ""); } set { if (v1Task != null) v1Task.SetParameters(value); else SetProperty(nameof(Arguments), value); } } /// Gets or sets the path to an executable file. [XmlElement("Command")] [DefaultValue("")] public string Path { get { if (v1Task != null) return v1Task.GetApplicationName(); return GetProperty(nameof(Path), ""); } set { if (v1Task != null) v1Task.SetApplicationName(value); else SetProperty(nameof(Path), value); } } /// /// Gets or sets the directory that contains either the executable file or the files that are used by the executable file. /// [DefaultValue("")] public string WorkingDirectory { get { if (v1Task != null) return v1Task.GetWorkingDirectory(); return GetProperty(nameof(WorkingDirectory), ""); } set { if (v1Task != null) v1Task.SetWorkingDirectory(value); else SetProperty(nameof(WorkingDirectory), value); } } internal override TaskActionType InternalActionType => TaskActionType.Execute; /// Determines whether the specified path is a valid filename and, optionally, if it exists. /// The path. /// if set to true check if file exists. /// if set to true throw exception on error. /// true if the specified path is a valid filename; otherwise, false. public static bool IsValidPath(string path, bool checkIfExists = true, bool throwOnException = false) { try { if (path == null) throw new ArgumentNullException(nameof(path)); /*if (path.StartsWith("\"") && path.EndsWith("\"") && path.Length > 1) path = path.Substring(1, path.Length - 2);*/ var fn = System.IO.Path.GetFileName(path); System.Diagnostics.Debug.WriteLine($"IsValidPath fn={fn}"); if (fn == string.Empty) return false; var dn = System.IO.Path.GetDirectoryName(path); System.Diagnostics.Debug.WriteLine($"IsValidPath dir={dn ?? "null"}"); System.IO.Path.GetFullPath(path); return true; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"IsValidPath exc={ex}"); if (throwOnException) throw; } return false; } /// Indicates whether the current object is equal to another object of the same type. /// An object to compare with this object. /// true if the current object is equal to the parameter; otherwise, false. public override bool Equals(Action other) => base.Equals(other) && Path == ((ExecAction)other).Path && Arguments == ((ExecAction)other).Arguments && WorkingDirectory == ((ExecAction)other).WorkingDirectory; /// /// Validates the input as a valid filename and optionally checks for its existence. If valid, the property is /// set to the validated absolute file path. /// /// The file path to validate. /// if set to true check if the file exists. public void SetValidatedPath([NotNull] string path, bool checkIfExists = true) { if (IsValidPath(path, checkIfExists, true)) Path = path; } /// Gets a string representation of the . /// String representation of this action. public override string ToString() => string.Format(Properties.Resources.ExecAction, Path, Arguments, WorkingDirectory, Id); internal static string BuildPowerShellCmd(string actionType, string cmd) => string.Format(PowerShellArgFormat, ScriptIdentifer, actionType, cmd); internal static ExecAction ConvertToPowerShellAction(Action action) => CreatePowerShellAction(action.ActionType.ToString(), action.GetPowerShellCommand()); internal static ExecAction CreatePowerShellAction(string actionType, string cmd) => new ExecAction(PowerShellPath, BuildPowerShellCmd(actionType, cmd)); internal static Action FromPowerShellCommand(string p) { var match = System.Text.RegularExpressions.Regex.Match(p, "^Start-Process -FilePath '(?

[^']*)'(?: -ArgumentList '(?[^']*)')?(?: -WorkingDirectory '(?[^']*)')?;?\\s*$"); return match.Success ? new ExecAction(match.Groups["p"].Value, match.Groups["a"].Success ? match.Groups["a"].Value.Replace("''", "'") : null, match.Groups["d"].Success ? match.Groups["d"].Value : null) : null; } ///

Copies the properties from another the current instance. /// The source . internal override void CopyProperties(Action sourceAction) { if (sourceAction.GetType() == GetType()) { base.CopyProperties(sourceAction); Path = ((ExecAction)sourceAction).Path; Arguments = ((ExecAction)sourceAction).Arguments; WorkingDirectory = ((ExecAction)sourceAction).WorkingDirectory; } } internal override void CreateV2Action(IActionCollection iActions) => iAction = iActions.Create(TaskActionType.Execute); internal override string GetPowerShellCommand() { var sb = new System.Text.StringBuilder($"Start-Process -FilePath '{Path}'"); if (!string.IsNullOrEmpty(Arguments)) sb.Append($" -ArgumentList '{Arguments.Replace("'", "''")}'"); if (!string.IsNullOrEmpty(WorkingDirectory)) sb.Append($" -WorkingDirectory '{WorkingDirectory}'"); return sb.Append("; ").ToString(); } internal string[] ParsePowerShellItems() { if (((Path?.EndsWith(PowerShellPath, StringComparison.InvariantCultureIgnoreCase) ?? false) || (Path?.EndsWith(PowerShellPath + ".exe", StringComparison.InvariantCultureIgnoreCase) ?? false)) && (Arguments?.Contains(ScriptIdentifer) ?? false)) { var match = System.Text.RegularExpressions.Regex.Match(Arguments, @"<# " + ScriptIdentifer + ":(?\\w+) #> (?.+)}\"$"); if (match.Success) return new[] { match.Groups["type"].Value, match.Groups["cmd"].Value }; } return null; } } /// /// Represents an action that shows a message box when a task is activated. Only available for Task Scheduler 2.0 on Windows Vista /// or Windows Server 2003 and later.This action has been deprecated in Windows 8 and later. However, this /// library is able to mimic its functionality using PowerShell if the property is /// set to . To disable this conversion, set the value to . /// /// Display a message when the trigger fires using the ShowMessageAction. /// /// /// /// /// [XmlType(IncludeInSchema = true)] [XmlRoot("ShowMessage", Namespace = TaskDefinition.tns, IsNullable = false)] public sealed class ShowMessageAction : Action, IBindAsExecAction { /// Creates a new unbound instance of . public ShowMessageAction() { } /// Creates a new unbound instance of . /// Message text that is displayed in the body of the message box. /// Title of the message box. public ShowMessageAction([CanBeNull] string messageBody, [CanBeNull] string title) { MessageBody = messageBody; Title = title; } internal ShowMessageAction([NotNull] V1Interop.ITask task) : base(task) { } internal ShowMessageAction([NotNull] IAction action) : base(action) { } /// Gets or sets the message text that is displayed in the body of the message box. [XmlElement("Body")] [DefaultValue(null)] public string MessageBody { get => GetProperty(nameof(MessageBody)); set => SetProperty(nameof(MessageBody), value); } /// Gets or sets the title of the message box. [DefaultValue(null)] public string Title { get => GetProperty(nameof(Title)); set => SetProperty(nameof(Title), value); } internal override TaskActionType InternalActionType => TaskActionType.ShowMessage; /// Indicates whether the current object is equal to another object of the same type. /// An object to compare with this object. /// true if the current object is equal to the parameter; otherwise, false. public override bool Equals(Action other) => base.Equals(other) && string.Equals(Title, (other as ShowMessageAction)?.Title) && string.Equals(MessageBody, (other as ShowMessageAction)?.MessageBody); /// Gets a string representation of the . /// String representation of this action. public override string ToString() => string.Format(Properties.Resources.ShowMessageAction, Title, MessageBody, Id); internal static Action FromPowerShellCommand(string p) { var match = System.Text.RegularExpressions.Regex.Match(p, @"^\[System.Reflection.Assembly\]::LoadWithPartialName\('System.Windows.Forms'\); \[System.Windows.Forms.MessageBox\]::Show\('(?(?:[^']|'')*)'(?:,'(?(?:[^']|'')*)')?\);?\s*$"); return match.Success ? new ShowMessageAction(match.Groups["msg"].Value.Replace("''", "'"), match.Groups["t"].Success ? match.Groups["t"].Value.Replace("''", "'") : null) : null; } /// Copies the properties from another the current instance. /// The source . internal override void CopyProperties(Action sourceAction) { if (sourceAction.GetType() == GetType()) { base.CopyProperties(sourceAction); Title = ((ShowMessageAction)sourceAction).Title; MessageBody = ((ShowMessageAction)sourceAction).MessageBody; } } internal override void CreateV2Action(IActionCollection iActions) => iAction = iActions.Create(TaskActionType.ShowMessage); internal override string GetPowerShellCommand() { // [System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Your_Desired_Message','Your_Desired_Title'); var sb = new System.Text.StringBuilder("[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('"); sb.Append(MessageBody.Replace("'", "''")); if (Title != null) { sb.Append("','"); sb.Append(Title.Replace("'", "''")); } sb.Append("'); "); return sb.ToString(); } } }