<commit_before>﻿using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace Microsoft.Win32.TaskScheduler
{
    #region Enumerations

    /// <summary>Defines what versions of Task Scheduler or the AT command that the task is compatible with.</summary>
    public enum TaskCompatibility
    {
        /// <summary>The task is compatible with the AT command.</summary>
        AT,
        /// <summary>The task is compatible with Task Scheduler 1.0.</summary>
        V1,
        /// <summary>The task is compatible with Task Scheduler 2.0.</summary>
        V2
    }

    /// <summary>Defines how the Task Scheduler service creates, updates, or disables the task.</summary>
    public enum TaskCreation
    {
        /// <summary>The Task Scheduler service registers the task as a new task.</summary>
        Create = 2,
        /// <summary>The Task Scheduler service either registers the task as a new task or as an updated version if the task already exists. Equivalent to Create | Update.</summary>
        CreateOrUpdate = 6,
        /// <summary>The Task Scheduler service registers the disabled task. A disabled task cannot run until it is enabled. For more information, see Enabled Property of TaskSettings and Enabled Property of RegisteredTask.</summary>
        Disable = 8,
        /// <summary>The Task Scheduler service is prevented from adding the allow access-control entry (ACE) for the context principal. When the TaskFolder.RegisterTaskDefinition or TaskFolder.RegisterTask functions are called with this flag to update a task, the Task Scheduler service does not add the ACE for the new context principal and does not remove the ACE from the old context principal.</summary>
        DontAddPrincipalAce = 0x10,
        /// <summary>The Task Scheduler service creates the task, but ignores the registration triggers in the task. By ignoring the registration triggers, the task will not execute when it is registered unless a time-based trigger causes it to execute on registration.</summary>
        IgnoreRegistrationTriggers = 0x20,
        /// <summary>The Task Scheduler service registers the task as an updated version of an existing task. When a task with a registration trigger is updated, the task will execute after the update occurs.</summary>
        Update = 4,
        /// <summary>The Task Scheduler service checks the syntax of the XML that describes the task but does not register the task. This constant cannot be combined with the Create, Update, or CreateOrUpdate values.</summary>
        ValidateOnly = 1
    }

    /// <summary>Defines how the Task Scheduler handles existing instances of the task when it starts a new instance of the task.</summary>
    public enum TaskInstancesPolicy
    {
        /// <summary>Starts new instance while an existing instance is running.</summary>
        Parallel,
        /// <summary>Starts a new instance of the task after all other instances of the task are complete.</summary>
        Queue,
        /// <summary>Does not start a new instance if an existing instance of the task is running.</summary>
        IgnoreNew,
        /// <summary>Stops an existing instance of the task before it starts a new instance.</summary>
        StopExisting
    }

    /// <summary>Defines what logon technique is required to run a task.</summary>
    public enum TaskLogonType
    {
        /// <summary>The logon method is not specified. Used for non-NT credentials.</summary>
        None,
        /// <summary>Use a password for logging on the user. The password must be supplied at registration time.</summary>
        Password,
        /// <summary>Use an existing interactive token to run a task. The user must log on using a service for user (S4U) logon. When an S4U logon is used, no password is stored by the system and there is no access to either the network or to encrypted files.</summary>
        S4U,
        /// <summary>User must already be logged on. The task will be run only in an existing interactive session.</summary>
        InteractiveToken,
        /// <summary>Group activation. The groupId field specifies the group.</summary>
        Group,
        /// <summary>Indicates that a Local System, Local Service, or Network Service account is being used as a security context to run the task.</summary>
        ServiceAccount,
        /// <summary>First use the interactive token. If the user is not logged on (no interactive token is available), then the password is used. The password must be specified when a task is registered. This flag is not recommended for new tasks because it is less reliable than Password.</summary>
        InteractiveTokenOrPassword
    }

    /// <summary>Defines the types of process security identifier (SID) that can be used by tasks. These changes are used to specify the type of process SID in the IPrincipal2 interface.</summary>
    public enum TaskProcessTokenSidType
    {
        /// <summary>No changes will be made to the process token groups list.</summary>
        None = 0,
        /// <summary>A task SID that is derived from the task name will be added to the process token groups list, and the token default discretionary access control list (DACL) will be modified to allow only the task SID and local system full control and the account SID read control.</summary>
        Unrestricted = 1,
        /// <summary>A Task Scheduler will apply default settings to the task process.</summary>
        Default = 2
    }

    /// <summary>Defines how a task is run.</summary>
    public enum TaskRunFlags
    {
        /// <summary>The task is run as the user who is calling the Run method.</summary>
        AsSelf = 1,
        /// <summary>The task is run regardless of constraints such as "do not run on batteries" or "run only if idle".</summary>
        IgnoreConstraints = 2,
        /// <summary>The task is run with all flags ignored.</summary>
        NoFlags = 0,
        /// <summary>The task is run using a terminal server session identifier.</summary>
        UseSessionId = 4,
        /// <summary>The task is run using a security identifier.</summary>
        UserSID = 8
    }

    /// <summary>Defines LUA elevation flags that specify with what privilege level the task will be run.</summary>
    public enum TaskRunLevel
    {
        /// <summary>Tasks will be run with the least privileges.</summary>
        LUA,
        /// <summary>Tasks will be run with the highest privileges.</summary>
        Highest
    }

    /// <summary>Defines what kind of Terminal Server session state change you can use to trigger a task to start. These changes are used to specify the type of state change in the SessionStateChangeTrigger.</summary>
    public enum TaskSessionStateChangeType
    {
        /// <summary>Terminal Server console connection state change. For example, when you connect to a user session on the local computer by switching users on the computer.</summary>
        ConsoleConnect = 1,
        /// <summary>Terminal Server console disconnection state change. For example, when you disconnect to a user session on the local computer by switching users on the computer.</summary>
        ConsoleDisconnect = 2,
        /// <summary>Terminal Server remote connection state change. For example, when a user connects to a user session by using the Remote Desktop Connection program from a remote computer.</summary>
        RemoteConnect = 3,
        /// <summary>Terminal Server remote disconnection state change. For example, when a user disconnects from a user session while using the Remote Desktop Connection program from a remote computer.</summary>
        RemoteDisconnect = 4,
        /// <summary>Terminal Server session locked state change. For example, this state change causes the task to run when the computer is locked.</summary>
        SessionLock = 7,
        /// <summary>Terminal Server session unlocked state change. For example, this state change causes the task to run when the computer is unlocked.</summary>
        SessionUnlock = 8
    }

    /// <summary>Defines the different states that a registered task can be in.</summary>
    public enum TaskState
    {
        /// <summary>The state of the task is unknown.</summary>
        Unknown,
        /// <summary>The task is registered but is disabled and no instances of the task are queued or running. The task cannot be run until it is enabled.</summary>
        Disabled,
        /// <summary>Instances of the task are queued.</summary>
        Queued,
        /// <summary>The task is ready to be executed, but no instances are queued or running.</summary>
        Ready,
        /// <summary>One or more instances of the task is running.</summary>
        Running
    }

    #endregion Enumerations

    /// <summary>
    /// Specifies how the Task Scheduler performs tasks when the computer is in an idle condition. For information about idle conditions, see Task Idle Conditions.
    /// </summary>
    public sealed class IdleSettings : IDisposable
    {
        private V1Interop.ITask v1Task = null;
        private V2Interop.IIdleSettings v2Settings;

        internal IdleSettings(V2Interop.IIdleSettings iSettings)
        {
            v2Settings = iSettings;
        }

        internal IdleSettings(TaskScheduler.V1Interop.ITask iTask)
        {
            v1Task = iTask;
        }

        /// <summary>
        /// Gets or sets a value that indicates the amount of time that the computer must be in an idle state before the task is run.
        /// </summary>
        public TimeSpan IdleDuration
        {
            get
            {
                if (v2Settings != null)
                    return Task.StringToTimeSpan(v2Settings.IdleDuration);
                ushort idleMin, deadMin;
                v1Task.GetIdleWait(out idleMin, out deadMin);
                return TimeSpan.FromMinutes((double)deadMin);
            }
            set
            {
                if (v2Settings != null)
                    v2Settings.IdleDuration = Task.TimeSpanToString(value);
                else
                {
                    v1Task.SetIdleWait((ushort)this.WaitTimeout.TotalMinutes, (ushort)value.TotalMinutes);
                }
            }
        }

        /// <summary>
        /// Gets or sets a Boolean value that indicates whether the task is restarted when the computer cycles into an idle condition more than once.
        /// </summary>
        public bool RestartOnIdle
        {
            get
            {
                if (v2Settings != null)
                    return v2Settings.RestartOnIdle;
                return (v1Task.GetFlags() & V1Interop.TaskFlags.RestartOnIdleResume) == V1Interop.TaskFlags.RestartOnIdleResume;
            }
            set
            {
                if (v2Settings != null)
                    v2Settings.RestartOnIdle = value;
                else
                {
                    V1Interop.TaskFlags flags = v1Task.GetFlags();
                    if (value)
                        v1Task.SetFlags(flags |= V1Interop.TaskFlags.RestartOnIdleResume);
                    else
                        v1Task.SetFlags(flags &= ~V1Interop.TaskFlags.RestartOnIdleResume);
                }
            }
        }

        /// <summary>
        /// Gets or sets a Boolean value that indicates that the Task Scheduler will terminate the task if the idle condition ends before the task is completed.
        /// </summary>
        public bool StopOnIdleEnd
        {
            get
            {
                if (v2Settings != null)
                    return v2Settings.StopOnIdleEnd;
                return (v1Task.GetFlags() & V1Interop.TaskFlags.KillOnIdleEnd) == V1Interop.TaskFlags.KillOnIdleEnd;
            }
            set
            {
                if (v2Settings != null)
                    v2Settings.StopOnIdleEnd = value;
                else
                {
                    V1Interop.TaskFlags flags = v1Task.GetFlags();
                    if (value)
                        v1Task.SetFlags(flags |= V1Interop.TaskFlags.KillOnIdleEnd);
                    else
                        v1Task.SetFlags(flags &= ~V1Interop.TaskFlags.KillOnIdleEnd);
                }
            }
        }

        /// <summary>
        /// Gets or sets a value that indicates the amount of time that the Task Scheduler will wait for an idle condition to occur.
        /// </summary>
        public TimeSpan WaitTimeout
        {
            get
            {
                if (v2Settings != null)
                    return Task.StringToTimeSpan(v2Settings.WaitTimeout);
                ushort idleMin, deadMin;
                v1Task.GetIdleWait(out idleMin, out deadMin);
                return TimeSpan.FromMinutes(idleMin);
            }
            set
            {
                if (v2Settings != null)
                    v2Settings.WaitTimeout = Task.TimeSpanToString(value);
                else
                {
                    v1Task.SetIdleWait((ushort)value.TotalMinutes, (ushort)this.IdleDuration.TotalMinutes);
                }
            }
        }

        /// <summary>
        /// Releases all resources used by this class.
        /// </summary>
        public void Dispose()
        {
            if (v2Settings != null)
                Marshal.ReleaseComObject(v2Settings);
            v1Task = null;
        }
    }

    /// <summary>
    /// Provides the settings that the Task Scheduler service uses to obtain a network profile.
    /// </summary>
    public sealed class NetworkSettings : IDisposable
    {
        private V2Interop.INetworkSettings v2Settings = null;

        internal NetworkSettings(V2Interop.INetworkSettings iSettings)
        {
            v2Settings = iSettings;
        }

        internal NetworkSettings()
        {
        }

        /// <summary>
        /// Gets or sets a GUID value that identifies a network profile.
        /// </summary>
        public Guid Id
        {
            get
            {
                string id = null;
                if (v2Settings != null)
                    id = v2Settings.Id;
                return string.IsNullOrEmpty(id) ? Guid.Empty : new Guid(id);
            }
            set
            {
                if (v2Settings != null)
                    v2Settings.Id = value.ToString();
                else
                    throw new NotV1SupportedException();
            }
        }

        /// <summary>
        /// Gets or sets the name of a network profile. The name is used for display purposes.
        /// </summary>
        public string Name
        {
            get
            {
                if (v2Settings != null)
                    return v2Settings.Name;
                return null;
            }
            set
            {
                if (v2Settings != null)
                    v2Settings.Name = value;
                else
                    throw new NotV1SupportedException();
            }
        }

        /// <summary>
        /// Releases all resources used by this class.
        /// </summary>
        public void Dispose()
        {
            if (v2Settings != null)
                Marshal.ReleaseComObject(v2Settings);
        }
    }

    /// <summary>
    /// Provides the methods to get information from and control a running task.
    /// </summary>
    public sealed class RunningTask : Task
    {
        private TaskScheduler.V2Interop.IRunningTask v2RunningTask;

        internal RunningTask(TaskService svc, V2Interop.IRegisteredTask iTask, V2Interop.IRunningTask iRunningTask)
            : base(svc, iTask)
        {
            v2RunningTask = iRunningTask;
        }

        internal RunningTask(TaskService svc, V1Interop.ITask iTask)
            : base(svc, iTask)
        {
        }

        /// <summary>
        /// Gets the name of the current action that the running task is performing.
        /// </summary>
        public string CurrentAction
        {
            get
            {
                if (v2RunningTask != null)
                    return v2RunningTask.CurrentAction;
                return base.v1Task.GetApplicationName();
            }
        }

        /// <summary>
        /// Gets the process ID for the engine (process) which is running the task.
        /// </summary>
        public uint EnginePID
        {
            get
            {
                if (v2RunningTask != null)
                    return v2RunningTask.EnginePID;
                throw new NotV1SupportedException();
            }
        }

        /// <summary>
        /// Gets the GUID identifier for this instance of the task.
        /// </summary>
        public Guid InstanceGuid
        {
            get
            {
                if (v2RunningTask != null)
                    return new Guid(v2RunningTask.InstanceGuid);
                return Guid.Empty;
            }
        }

        /// <summary>
        /// Releases all resources used by this class.
        /// </summary>
        public new void Dispose()
        {
            base.Dispose();
            if (v2RunningTask != null) Marshal.ReleaseComObject(v2RunningTask);
        }

        /// <summary>
        /// Refreshes all of the local instance variables of the task.
        /// </summary>
        public void Refresh()
        {
            if (v2RunningTask != null)
                v2RunningTask.Refresh();
        }
    }

    /// <summary>
    /// Provides the methods that are used to run the task immediately, get any running instances of the task, get or set the credentials that are used to register the task, and the properties that describe the task.
    /// </summary>
    public class Task : IDisposable
    {
        internal V1Interop.ITask v1Task;

        private static readonly DateTime v2InvalidDate = new DateTime(1899, 12, 30);

        private V2Interop.IRegisteredTask v2Task;
		private TaskDefinition myTD;

        internal Task(TaskService svc, TaskScheduler.V1Interop.ITask iTask)
        {
            this.TaskService = svc;
            v1Task = iTask;
        }

        internal Task(TaskService svc, TaskScheduler.V2Interop.IRegisteredTask iTask)
        {
            this.TaskService = svc;
            v2Task = iTask;
        }

        /// <summary>
        /// Gets the definition of the task.
        /// </summary>
        public TaskDefinition Definition
        {
            get
            {
				if (myTD == null)
				{
					if (v2Task != null)
						myTD = new TaskDefinition(v2Task.Definition);
					else
						myTD = new TaskDefinition(v1Task, this.Name);
				}
				return myTD;
            }
        }

        /// <summary>
        /// Gets or sets a Boolean value that indicates if the registered task is enabled.
        /// </summary>
        public bool Enabled
        {
            get
            {
                if (v2Task != null)
                    return v2Task.Enabled;
                return (v1Task.GetFlags() & V1Interop.TaskFlags.Disabled) != V1Interop.TaskFlags.Disabled;
            }
            set
            {
                if (v2Task != null)
                    v2Task.Enabled = value;
                else
                {
                    V1Interop.TaskFlags flags = v1Task.GetFlags();
                    if (!value)
                        v1Task.SetFlags(flags |= V1Interop.TaskFlags.Disabled);
                    else
                        v1Task.SetFlags(flags &= ~V1Interop.TaskFlags.Disabled);
                }
            }
        }

        /*public Microsoft.Win32.TaskScheduler.InternalV2.IRunningTaskCollection GetInstances(int flags)
        {
            if (v2Task != null)
                return v2Task.GetInstances(flags);
            throw new NotV1SupportedException();
        }*/
        /// <summary>
        /// Gets the time the registered task was last run.
        /// </summary>
        public DateTime LastRunTime
        {
            get
            {
                if (v2Task != null)
                {
                    DateTime dt = v2Task.LastRunTime;
                    return dt == v2InvalidDate ? DateTime.MinValue : dt;
                }
                return v1Task.GetMostRecentRunTime();
            }
        }

        /// <summary>
        /// Gets the results that were returned the last time the registered task was run.
        /// </summary>
        public int LastTaskResult
        {
            get
            {
                if (v2Task != null)
                    return v2Task.LastTaskResult;
                return (int)v1Task.GetExitCode();
            }
        }

        /// <summary>
        /// Gets the name of the registered task.
        /// </summary>
        public string Name
        {
            get
            {
                if (v2Task != null)
                    return v2Task.Name;
                return System.IO.Path.GetFileNameWithoutExtension(GetV1Path(v1Task));
            }
        }

        /// <summary>
        /// Gets the time when the registered task is next scheduled to run.
        /// </summary>
        public DateTime NextRunTime
        {
            get
            {
                if (v2Task != null)
                    return v2Task.NextRunTime;
                return v1Task.GetNextRunTime();
            }
        }

        /// <summary>
        /// Gets the number of times the registered task has missed a scheduled run.
        /// </summary>
        public int NumberOfMissedRuns
        {
            get
            {
                if (v2Task != null)
                    return v2Task.NumberOfMissedRuns;

                throw new NotV1SupportedException();
            }
        }

        /// <summary>
        /// Gets the path to where the registered task is stored.
        /// </summary>
        public string Path
        {
            get
            {
                if (v2Task != null)
                    return v2Task.Path;
                return GetV1Path(v1Task);
            }
        }

		/// <summary>
		/// Gets or sets the security descriptor for the task.
		/// </summary>
		/// <value>The security descriptor.</value>
		public System.Security.AccessControl.GenericSecurityDescriptor SecurityDescriptor
		{
			get
			{
				string sddl = GetSecurityDescriptorSddlForm(System.Security.AccessControl.AccessControlSections.All);
				return new System.Security.AccessControl.RawSecurityDescriptor(sddl);
			}
			set
			{
				SetSecurityDescriptorSddlForm(value.GetSddlForm(System.Security.AccessControl.AccessControlSections.All), System.Security.AccessControl.AccessControlSections.All);
			}
		}

        /// <summary>
        /// Gets the operational state of the registered task.
        /// </summary>
        public TaskState State
        {
            get
            {
                if (v2Task != null)
                    return v2Task.State;

                switch (v1Task.GetStatus())
                {
                    case V1Interop.TaskStatus.Ready:
                    case V1Interop.TaskStatus.NeverRun:
                    case V1Interop.TaskStatus.NoMoreRuns:
                    case V1Interop.TaskStatus.Terminated:
                        return TaskState.Ready;
                    case V1Interop.TaskStatus.Running:
                        return TaskState.Running;
                    case V1Interop.TaskStatus.Disabled:
                        return TaskState.Disabled;
                    case V1Interop.TaskStatus.NotScheduled:
                    case V1Interop.TaskStatus.NoTriggers:
                    case V1Interop.TaskStatus.NoTriggerTime:
                    default:
                        return TaskState.Unknown;
                }
            }
        }

        /// <summary>
        /// Gets or sets the <see cref="TaskService"/> that manages this task.
        /// </summary>
        /// <value>The task service.</value>
        public TaskService TaskService
        {
            get; private set;
        }

        /// <summary>
        /// Gets the XML-formatted registration information for the registered task. Not available with Task Scheduler 1.0.
        /// </summary>
        public string Xml
        {
            get
            {
                if (v2Task != null)
                    return v2Task.Xml;

                throw new NotV1SupportedException();
            }
        }

        /// <summary>
        /// Releases all resources used by this class.
        /// </summary>
        public void Dispose()
        {
            if (v2Task != null)
                Marshal.ReleaseComObject(v2Task);
            v1Task = null;
        }

        /// <summary>
        /// Gets the times that the registered task is scheduled to run during a specified time.
		/// </summary>
        /// <param name="start">The starting time for the query.</param>
        /// <param name="end">The ending time for the query.</param>
		/// <param name="count">The requested number of runs. A value of 0 will return all times requested.</param>
        /// <returns>The scheduled times that the task will run.</returns>
        public DateTime[] GetRunTimes(DateTime start, DateTime end, uint count = 0)
        {
			const ushort TASK_MAX_RUN_TIMES = 1440;

			TaskScheduler.V1Interop.SystemTime stStart = new TaskScheduler.V1Interop.SystemTime(start);
            TaskScheduler.V1Interop.SystemTime stEnd = new TaskScheduler.V1Interop.SystemTime(end);
            IntPtr runTimes = IntPtr.Zero, st;
			if (v2Task != null)
				v2Task.GetRunTimes(stStart, stEnd, ref count, ref runTimes);
			else
			{
				ushort count1 = (count > 0 && count <= TASK_MAX_RUN_TIMES) ? (ushort)count : TASK_MAX_RUN_TIMES;
				v1Task.GetRunTimes(ref stStart, ref stEnd, ref count1, ref runTimes);
				count = count1;
			}
            DateTime[] ret = new DateTime[count];
            for (int i = 0; i < count; i++)
            {
				st = new IntPtr(runTimes.ToInt64() + (i * Marshal.SizeOf(typeof(TaskScheduler.V1Interop.SystemTime))));
                ret[i] = (TaskScheduler.V1Interop.SystemTime)Marshal.PtrToStructure(st, typeof(TaskScheduler.V1Interop.SystemTime));
            }
            Marshal.FreeCoTaskMem(runTimes);
            return ret;
        }

        /// <summary>
        /// Gets the security descriptor for the task. Not available to Task Scheduler 1.0.
        /// </summary>
        /// <param name="includeSections">Section(s) of the security descriptor to return.</param>
        /// <returns>The security descriptor for the task.</returns>
        public string GetSecurityDescriptorSddlForm(System.Security.AccessControl.AccessControlSections includeSections)
        {
            if (v2Task != null)
                return v2Task.GetSecurityDescriptor((int)includeSections);

            throw new NotV1SupportedException();
        }

		/// <summary>
		/// Updates the task with any changes made to the <see cref="Definition"/> by calling <see cref="TaskFolder.RegisterTaskDefinition(string, TaskDefinition)"/> from the currently registered folder using the currently registered name.
		/// </summary>
		/// <exception cref="System.Security.SecurityException">Thrown if task was previously registered with a password.</exception>
		public void RegisterChanges()
		{
			if (this.Definition.Principal.LogonType == TaskLogonType.InteractiveTokenOrPassword || this.Definition.Principal.LogonType == TaskLogonType.Password || this.Definition.Principal.LogonType == TaskLogonType.Group)
				throw new System.Security.SecurityException("Tasks which have been registered previously with stored passwords must use the TaskFolder.RegisterTaskDefinition method for updates.");
			TaskService.GetFolder(System.IO.Path.GetDirectoryName(this.Path)).RegisterTaskDefinition(this.Name, this.Definition);
		}

        /// <summary>
        /// Runs the registered task immediately.
        /// </summary>
        /// <param name="parameters">The parameters used as values in the task actions.</param>
        /// <returns>A <see cref="RunningTask"/> instance that defines the new instance of the task.</returns>
        public RunningTask Run(params string[] parameters)
        {
            if (v2Task != null)
                return new RunningTask(this.TaskService, this.v2Task, v2Task.Run(parameters));

            v1Task.Run();
            return new RunningTask(this.TaskService, this.v1Task);
        }

        /// <summary>
        /// Runs the registered task immediately using specified flags and a session identifier.
        /// </summary>
        /// <param name="flags">Defines how the task is run.</param>
        /// <param name="sessionID">The terminal server session in which you want to start the task.</param>
        /// <param name="user">The user for which the task runs.</param>
        /// <param name="parameters">The parameters used as values in the task actions.</param>
        /// <returns>A <see cref="RunningTask"/> instance that defines the new instance of the task.</returns>
        public RunningTask RunEx(TaskRunFlags flags, int sessionID, string user, params string[] parameters)
        {
            if (v2Task != null)
                return new RunningTask(this.TaskService, this.v2Task, v2Task.RunEx(parameters, (int)flags, sessionID, user));
            throw new NotV1SupportedException();
        }

        /// <summary>
        /// Sets the security descriptor for the task. Not available to Task Scheduler 1.0.
        /// </summary>
        /// <param name="sddlForm">The security descriptor for the task.</param>
        /// <param name="includeSections">Section(s) of the security descriptor to set.</param>
        public void SetSecurityDescriptorSddlForm(string sddlForm, System.Security.AccessControl.AccessControlSections includeSections)
        {
            if (v2Task != null)
                v2Task.SetSecurityDescriptor(sddlForm, (int)includeSections);

            throw new NotV1SupportedException();
        }

        /// <summary>
        /// Shows the property page for the task (v1.0 only).
        /// </summary>
        public void ShowPropertyPage()
        {
            if (v1Task != null)
                v1Task.EditWorkItem(IntPtr.Zero, 0);
            else
                throw new NotV2SupportedException();
        }

        /// <summary>
        /// Stops the registered task immediately.
        /// </summary>
        public void Stop()
        {
            if (v2Task != null)
                v2Task.Stop(0);
            v1Task.Terminate();
        }<commit_msg>* Added parameter validation to Task.Run * Fixed bug in Task.Stop (thanks SergeyI)<commit_after>﻿using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace Microsoft.Win32.TaskScheduler
{
    #region Enumerations

    /// <summary>Defines what versions of Task Scheduler or the AT command that the task is compatible with.</summary>
    public enum TaskCompatibility
    {
        /// <summary>The task is compatible with the AT command.</summary>
        AT,
        /// <summary>The task is compatible with Task Scheduler 1.0.</summary>
        V1,
        /// <summary>The task is compatible with Task Scheduler 2.0.</summary>
        V2
    }

    /// <summary>Defines how the Task Scheduler service creates, updates, or disables the task.</summary>
    public enum TaskCreation
    {
        /// <summary>The Task Scheduler service registers the task as a new task.</summary>
        Create = 2,
        /// <summary>The Task Scheduler service either registers the task as a new task or as an updated version if the task already exists. Equivalent to Create | Update.</summary>
        CreateOrUpdate = 6,
        /// <summary>The Task Scheduler service registers the disabled task. A disabled task cannot run until it is enabled. For more information, see Enabled Property of TaskSettings and Enabled Property of RegisteredTask.</summary>
        Disable = 8,
        /// <summary>The Task Scheduler service is prevented from adding the allow access-control entry (ACE) for the context principal. When the TaskFolder.RegisterTaskDefinition or TaskFolder.RegisterTask functions are called with this flag to update a task, the Task Scheduler service does not add the ACE for the new context principal and does not remove the ACE from the old context principal.</summary>
        DontAddPrincipalAce = 0x10,
        /// <summary>The Task Scheduler service creates the task, but ignores the registration triggers in the task. By ignoring the registration triggers, the task will not execute when it is registered unless a time-based trigger causes it to execute on registration.</summary>
        IgnoreRegistrationTriggers = 0x20,
        /// <summary>The Task Scheduler service registers the task as an updated version of an existing task. When a task with a registration trigger is updated, the task will execute after the update occurs.</summary>
        Update = 4,
        /// <summary>The Task Scheduler service checks the syntax of the XML that describes the task but does not register the task. This constant cannot be combined with the Create, Update, or CreateOrUpdate values.</summary>
        ValidateOnly = 1
    }

    /// <summary>Defines how the Task Scheduler handles existing instances of the task when it starts a new instance of the task.</summary>
    public enum TaskInstancesPolicy
    {
        /// <summary>Starts new instance while an existing instance is running.</summary>
        Parallel,
        /// <summary>Starts a new instance of the task after all other instances of the task are complete.</summary>
        Queue,
        /// <summary>Does not start a new instance if an existing instance of the task is running.</summary>
        IgnoreNew,
        /// <summary>Stops an existing instance of the task before it starts a new instance.</summary>
        StopExisting
    }

    /// <summary>Defines what logon technique is required to run a task.</summary>
    public enum TaskLogonType
    {
        /// <summary>The logon method is not specified. Used for non-NT credentials.</summary>
        None,
        /// <summary>Use a password for logging on the user. The password must be supplied at registration time.</summary>
        Password,
        /// <summary>Use an existing interactive token to run a task. The user must log on using a service for user (S4U) logon. When an S4U logon is used, no password is stored by the system and there is no access to either the network or to encrypted files.</summary>
        S4U,
        /// <summary>User must already be logged on. The task will be run only in an existing interactive session.</summary>
        InteractiveToken,
        /// <summary>Group activation. The groupId field specifies the group.</summary>
        Group,
        /// <summary>Indicates that a Local System, Local Service, or Network Service account is being used as a security context to run the task.</summary>
        ServiceAccount,
        /// <summary>First use the interactive token. If the user is not logged on (no interactive token is available), then the password is used. The password must be specified when a task is registered. This flag is not recommended for new tasks because it is less reliable than Password.</summary>
        InteractiveTokenOrPassword
    }

    /// <summary>Defines the types of process security identifier (SID) that can be used by tasks. These changes are used to specify the type of process SID in the IPrincipal2 interface.</summary>
    public enum TaskProcessTokenSidType
    {
        /// <summary>No changes will be made to the process token groups list.</summary>
        None = 0,
        /// <summary>A task SID that is derived from the task name will be added to the process token groups list, and the token default discretionary access control list (DACL) will be modified to allow only the task SID and local system full control and the account SID read control.</summary>
        Unrestricted = 1,
        /// <summary>A Task Scheduler will apply default settings to the task process.</summary>
        Default = 2
    }

    /// <summary>Defines how a task is run.</summary>
    public enum TaskRunFlags
    {
        /// <summary>The task is run as the user who is calling the Run method.</summary>
        AsSelf = 1,
        /// <summary>The task is run regardless of constraints such as "do not run on batteries" or "run only if idle".</summary>
        IgnoreConstraints = 2,
        /// <summary>The task is run with all flags ignored.</summary>
        NoFlags = 0,
        /// <summary>The task is run using a terminal server session identifier.</summary>
        UseSessionId = 4,
        /// <summary>The task is run using a security identifier.</summary>
        UserSID = 8
    }

    /// <summary>Defines LUA elevation flags that specify with what privilege level the task will be run.</summary>
    public enum TaskRunLevel
    {
        /// <summary>Tasks will be run with the least privileges.</summary>
        LUA,
        /// <summary>Tasks will be run with the highest privileges.</summary>
        Highest
    }

    /// <summary>Defines what kind of Terminal Server session state change you can use to trigger a task to start. These changes are used to specify the type of state change in the SessionStateChangeTrigger.</summary>
    public enum TaskSessionStateChangeType
    {
        /// <summary>Terminal Server console connection state change. For example, when you connect to a user session on the local computer by switching users on the computer.</summary>
        ConsoleConnect = 1,
        /// <summary>Terminal Server console disconnection state change. For example, when you disconnect to a user session on the local computer by switching users on the computer.</summary>
        ConsoleDisconnect = 2,
        /// <summary>Terminal Server remote connection state change. For example, when a user connects to a user session by using the Remote Desktop Connection program from a remote computer.</summary>
        RemoteConnect = 3,
        /// <summary>Terminal Server remote disconnection state change. For example, when a user disconnects from a user session while using the Remote Desktop Connection program from a remote computer.</summary>
        RemoteDisconnect = 4,
        /// <summary>Terminal Server session locked state change. For example, this state change causes the task to run when the computer is locked.</summary>
        SessionLock = 7,
        /// <summary>Terminal Server session unlocked state change. For example, this state change causes the task to run when the computer is unlocked.</summary>
        SessionUnlock = 8
    }

	/***** WAITING TO DETERMINE USE CASE *****
	/// <summary>
	/// Success and error codes that some methods will expose through <see cref="COMExcpetion"/>.
	/// </summary>
	public enum TaskResultCode
	{
		/// <summary>The task is ready to run at its next scheduled time.</summary>
		TaskReady = 0x00041300,
		/// <summary>The task is currently running.</summary>
		TaskRunning = 0x00041301,
		/// <summary>The task will not run at the scheduled times because it has been disabled.</summary>
		TaskDisabled = 0x00041302,
		/// <summary>The task has not yet run.</summary>
		TaskHasNotRun = 0x00041303,
		/// <summary>There are no more runs scheduled for this task.</summary>
		TaskNoMoreRuns = 0x00041304,
		/// <summary>One or more of the properties that are needed to run this task on a schedule have not been set.</summary>
		TaskNotScheduled = 0x00041305,
		/// <summary>The last run of the task was terminated by the user.</summary>
		TaskTerminated = 0x00041306,
		/// <summary>Either the task has no triggers or the existing triggers are disabled or not set.</summary>
		TaskNoValidTriggers = 0x00041307,
		/// <summary>Event triggers do not have set run times.</summary>
		EventTrigger = 0x00041308,
		/// <summary>A task's trigger is not found.</summary>
		TriggerNotFound = 0x80041309,
		/// <summary>One or more of the properties required to run this task have not been set.</summary>
		TaskNotReady = 0x8004130A,
		/// <summary>There is no running instance of the task.</summary>
		TaskNotRunning = 0x8004130B,
		/// <summary>The Task Scheduler service is not installed on this computer.</summary>
		ServiceNotInstalled = 0x8004130C,
		/// <summary>The task object could not be opened.</summary>
		CannotOpenTask = 0x8004130D,
		/// <summary>The object is either an invalid task object or is not a task object.</summary>
		InvalidTask = 0x8004130E,
		/// <summary>No account information could be found in the Task Scheduler security database for the task indicated.</summary>
		AccountInformationNotSet = 0x8004130F,
		/// <summary>Unable to establish existence of the account specified.</summary>
		AccountNameNotFound = 0x80041310,
		/// <summary>Corruption was detected in the Task Scheduler security database; the database has been reset.</summary>
		AccountDbaseCorrupt = 0x80041311,
		/// <summary>Task Scheduler security services are available only on Windows NT.</summary>
		NoSecurityServices = 0x80041312,
		/// <summary>The task object version is either unsupported or invalid.</summary>
		UnknownObjectVersion = 0x80041313,
		/// <summary>The task has been configured with an unsupported combination of account settings and run time options.</summary>
		UnsupportedAccountOption = 0x80041314,
		/// <summary>The Task Scheduler Service is not running.</summary>
		ServiceNotRunning = 0x80041315,
		/// <summary>The task XML contains an unexpected node.</summary>
		UnexpectedNode = 0x80041316,
		/// <summary>The task XML contains an element or attribute from an unexpected namespace.</summary>
		Namespace = 0x80041317,
		/// <summary>The task XML contains a value which is incorrectly formatted or out of range.</summary>
		InvalidValue = 0x80041318,
		/// <summary>The task XML is missing a required element or attribute.</summary>
		MissingNode = 0x80041319,
		/// <summary>The task XML is malformed.</summary>
		MalformedXml = 0x8004131A,
		/// <summary>The task is registered, but not all specified triggers will start the task.</summary>
		SomeTriggersFailed = 0x0004131B,
		/// <summary>The task is registered, but may fail to start. Batch logon privilege needs to be enabled for the task principal.</summary>
		BatchLogonProblem = 0x0004131C,
		/// <summary>The task XML contains too many nodes of the same type.</summary>
		TooManyNodes = 0x8004131D,
		/// <summary>The task cannot be started after the trigger end boundary.</summary>
		PastEndBoundary = 0x8004131E,
		/// <summary>An instance of this task is already running.</summary>
		AlreadyRunning = 0x8004131F,
		/// <summary>The task will not run because the user is not logged on.</summary>
		UserNotLoggedOn = 0x80041320,
		/// <summary>The task image is corrupt or has been tampered with.</summary>
		InvalidTaskHash = 0x80041321,
		/// <summary>The Task Scheduler service is not available.</summary>
		ServiceNotAvailable = 0x80041322,
		/// <summary>The Task Scheduler service is too busy to handle your request. Please try again later.</summary>
		ServiceTooBusy = 0x80041323,
		/// <summary>The Task Scheduler service attempted to run the task, but the task did not run due to one of the constraints in the task definition.</summary>
		TaskAttempted = 0x80041324,
		/// <summary>The Task Scheduler service has asked the task to run.</summary>
		TaskQueued = 0x00041325,
		/// <summary>The task is disabled.</summary>
		TaskDisabled = 0x80041326,
		/// <summary>The task has properties that are not compatible with earlier versions of Windows.</summary>
		TaskNotV1Compatible = 0x80041327,
		/// <summary>The task settings do not allow the task to start on demand.</summary>
		StartOnDemand = 0x80041328,
	}
	*/

    /// <summary>Defines the different states that a registered task can be in.</summary>
    public enum TaskState
    {
        /// <summary>The state of the task is unknown.</summary>
        Unknown,
        /// <summary>The task is registered but is disabled and no instances of the task are queued or running. The task cannot be run until it is enabled.</summary>
        Disabled,
        /// <summary>Instances of the task are queued.</summary>
        Queued,
        /// <summary>The task is ready to be executed, but no instances are queued or running.</summary>
        Ready,
        /// <summary>One or more instances of the task is running.</summary>
        Running
    }

    #endregion Enumerations

    /// <summary>
    /// Specifies how the Task Scheduler performs tasks when the computer is in an idle condition. For information about idle conditions, see Task Idle Conditions.
    /// </summary>
    public sealed class IdleSettings : IDisposable
    {
        private V1Interop.ITask v1Task = null;
        private V2Interop.IIdleSettings v2Settings;

        internal IdleSettings(V2Interop.IIdleSettings iSettings)
        {
            v2Settings = iSettings;
        }

        internal IdleSettings(TaskScheduler.V1Interop.ITask iTask)
        {
            v1Task = iTask;
        }

        /// <summary>
        /// Gets or sets a value that indicates the amount of time that the computer must be in an idle state before the task is run.
        /// </summary>
        public TimeSpan IdleDuration
        {
            get
            {
                if (v2Settings != null)
                    return Task.StringToTimeSpan(v2Settings.IdleDuration);
                ushort idleMin, deadMin;
                v1Task.GetIdleWait(out idleMin, out deadMin);
                return TimeSpan.FromMinutes((double)deadMin);
            }
            set
            {
                if (v2Settings != null)
                    v2Settings.IdleDuration = Task.TimeSpanToString(value);
                else
                {
                    v1Task.SetIdleWait((ushort)this.WaitTimeout.TotalMinutes, (ushort)value.TotalMinutes);
                }
            }
        }

        /// <summary>
        /// Gets or sets a Boolean value that indicates whether the task is restarted when the computer cycles into an idle condition more than once.
        /// </summary>
        public bool RestartOnIdle
        {
            get
            {
                if (v2Settings != null)
                    return v2Settings.RestartOnIdle;
                return (v1Task.GetFlags() & V1Interop.TaskFlags.RestartOnIdleResume) == V1Interop.TaskFlags.RestartOnIdleResume;
            }
            set
            {
                if (v2Settings != null)
                    v2Settings.RestartOnIdle = value;
                else
                {
                    V1Interop.TaskFlags flags = v1Task.GetFlags();
                    if (value)
                        v1Task.SetFlags(flags |= V1Interop.TaskFlags.RestartOnIdleResume);
                    else
                        v1Task.SetFlags(flags &= ~V1Interop.TaskFlags.RestartOnIdleResume);
                }
            }
        }

        /// <summary>
        /// Gets or sets a Boolean value that indicates that the Task Scheduler will terminate the task if the idle condition ends before the task is completed.
        /// </summary>
        public bool StopOnIdleEnd
        {
            get
            {
                if (v2Settings != null)
                    return v2Settings.StopOnIdleEnd;
                return (v1Task.GetFlags() & V1Interop.TaskFlags.KillOnIdleEnd) == V1Interop.TaskFlags.KillOnIdleEnd;
            }
            set
            {
                if (v2Settings != null)
                    v2Settings.StopOnIdleEnd = value;
                else
                {
                    V1Interop.TaskFlags flags = v1Task.GetFlags();
                    if (value)
                        v1Task.SetFlags(flags |= V1Interop.TaskFlags.KillOnIdleEnd);
                    else
                        v1Task.SetFlags(flags &= ~V1Interop.TaskFlags.KillOnIdleEnd);
                }
            }
        }

        /// <summary>
        /// Gets or sets a value that indicates the amount of time that the Task Scheduler will wait for an idle condition to occur.
        /// </summary>
        public TimeSpan WaitTimeout
        {
            get
            {
                if (v2Settings != null)
                    return Task.StringToTimeSpan(v2Settings.WaitTimeout);
                ushort idleMin, deadMin;
                v1Task.GetIdleWait(out idleMin, out deadMin);
                return TimeSpan.FromMinutes(idleMin);
            }
            set
            {
                if (v2Settings != null)
                    v2Settings.WaitTimeout = Task.TimeSpanToString(value);
                else
                {
                    v1Task.SetIdleWait((ushort)value.TotalMinutes, (ushort)this.IdleDuration.TotalMinutes);
                }
            }
        }

        /// <summary>
        /// Releases all resources used by this class.
        /// </summary>
        public void Dispose()
        {
            if (v2Settings != null)
                Marshal.ReleaseComObject(v2Settings);
            v1Task = null;
        }
    }

    /// <summary>
    /// Provides the settings that the Task Scheduler service uses to obtain a network profile.
    /// </summary>
    public sealed class NetworkSettings : IDisposable
    {
        private V2Interop.INetworkSettings v2Settings = null;

        internal NetworkSettings(V2Interop.INetworkSettings iSettings)
        {
            v2Settings = iSettings;
        }

        internal NetworkSettings()
        {
        }

        /// <summary>
        /// Gets or sets a GUID value that identifies a network profile.
        /// </summary>
        public Guid Id
        {
            get
            {
                string id = null;
                if (v2Settings != null)
                    id = v2Settings.Id;
                return string.IsNullOrEmpty(id) ? Guid.Empty : new Guid(id);
            }
            set
            {
                if (v2Settings != null)
                    v2Settings.Id = value.ToString();
                else
                    throw new NotV1SupportedException();
            }
        }

        /// <summary>
        /// Gets or sets the name of a network profile. The name is used for display purposes.
        /// </summary>
        public string Name
        {
            get
            {
                if (v2Settings != null)
                    return v2Settings.Name;
                return null;
            }
            set
            {
                if (v2Settings != null)
                    v2Settings.Name = value;
                else
                    throw new NotV1SupportedException();
            }
        }

        /// <summary>
        /// Releases all resources used by this class.
        /// </summary>
        public void Dispose()
        {
            if (v2Settings != null)
                Marshal.ReleaseComObject(v2Settings);
        }
    }

    /// <summary>
    /// Provides the methods to get information from and control a running task.
    /// </summary>
    public sealed class RunningTask : Task
    {
        private TaskScheduler.V2Interop.IRunningTask v2RunningTask;

        internal RunningTask(TaskService svc, V2Interop.IRegisteredTask iTask, V2Interop.IRunningTask iRunningTask)
            : base(svc, iTask)
        {
            v2RunningTask = iRunningTask;
        }

        internal RunningTask(TaskService svc, V1Interop.ITask iTask)
            : base(svc, iTask)
        {
        }

        /// <summary>
        /// Gets the name of the current action that the running task is performing.
        /// </summary>
        public string CurrentAction
        {
            get
            {
                if (v2RunningTask != null)
                    return v2RunningTask.CurrentAction;
                return base.v1Task.GetApplicationName();
            }
        }

        /// <summary>
        /// Gets the process ID for the engine (process) which is running the task.
        /// </summary>
        public uint EnginePID
        {
            get
            {
                if (v2RunningTask != null)
                    return v2RunningTask.EnginePID;
                throw new NotV1SupportedException();
            }
        }

        /// <summary>
        /// Gets the GUID identifier for this instance of the task.
        /// </summary>
        public Guid InstanceGuid
        {
            get
            {
                if (v2RunningTask != null)
                    return new Guid(v2RunningTask.InstanceGuid);
                return Guid.Empty;
            }
        }

        /// <summary>
        /// Releases all resources used by this class.
        /// </summary>
        public new void Dispose()
        {
            base.Dispose();
            if (v2RunningTask != null) Marshal.ReleaseComObject(v2RunningTask);
        }

        /// <summary>
        /// Refreshes all of the local instance variables of the task.
        /// </summary>
        public void Refresh()
        {
            if (v2RunningTask != null)
                v2RunningTask.Refresh();
        }
    }

    /// <summary>
    /// Provides the methods that are used to run the task immediately, get any running instances of the task, get or set the credentials that are used to register the task, and the properties that describe the task.
    /// </summary>
    public class Task : IDisposable
    {
        internal V1Interop.ITask v1Task;

        private static readonly DateTime v2InvalidDate = new DateTime(1899, 12, 30);

        private V2Interop.IRegisteredTask v2Task;
		private TaskDefinition myTD;

        internal Task(TaskService svc, TaskScheduler.V1Interop.ITask iTask)
        {
            this.TaskService = svc;
            v1Task = iTask;
        }

        internal Task(TaskService svc, TaskScheduler.V2Interop.IRegisteredTask iTask)
        {
            this.TaskService = svc;
            v2Task = iTask;
        }

        /// <summary>
        /// Gets the definition of the task.
        /// </summary>
        public TaskDefinition Definition
        {
            get
            {
				if (myTD == null)
				{
					if (v2Task != null)
						myTD = new TaskDefinition(v2Task.Definition);
					else
						myTD = new TaskDefinition(v1Task, this.Name);
				}
				return myTD;
            }
        }

        /// <summary>
        /// Gets or sets a Boolean value that indicates if the registered task is enabled.
        /// </summary>
        public bool Enabled
        {
            get
            {
                if (v2Task != null)
                    return v2Task.Enabled;
                return (v1Task.GetFlags() & V1Interop.TaskFlags.Disabled) != V1Interop.TaskFlags.Disabled;
            }
            set
            {
                if (v2Task != null)
                    v2Task.Enabled = value;
                else
                {
                    V1Interop.TaskFlags flags = v1Task.GetFlags();
                    if (!value)
                        v1Task.SetFlags(flags |= V1Interop.TaskFlags.Disabled);
                    else
                        v1Task.SetFlags(flags &= ~V1Interop.TaskFlags.Disabled);
                }
            }
        }

        /*public Microsoft.Win32.TaskScheduler.InternalV2.IRunningTaskCollection GetInstances(int flags)
        {
            if (v2Task != null)
                return v2Task.GetInstances(flags);
            throw new NotV1SupportedException();
        }*/
        /// <summary>
        /// Gets the time the registered task was last run.
        /// </summary>
        public DateTime LastRunTime
        {
            get
            {
                if (v2Task != null)
                {
                    DateTime dt = v2Task.LastRunTime;
                    return dt == v2InvalidDate ? DateTime.MinValue : dt;
                }
                return v1Task.GetMostRecentRunTime();
            }
        }

        /// <summary>
        /// Gets the results that were returned the last time the registered task was run.
        /// </summary>
        public int LastTaskResult
        {
            get
            {
                if (v2Task != null)
                    return v2Task.LastTaskResult;
                return (int)v1Task.GetExitCode();
            }
        }

        /// <summary>
        /// Gets the name of the registered task.
        /// </summary>
        public string Name
        {
            get
            {
                if (v2Task != null)
                    return v2Task.Name;
                return System.IO.Path.GetFileNameWithoutExtension(GetV1Path(v1Task));
            }
        }

        /// <summary>
        /// Gets the time when the registered task is next scheduled to run.
        /// </summary>
        public DateTime NextRunTime
        {
            get
            {
                if (v2Task != null)
                    return v2Task.NextRunTime;
                return v1Task.GetNextRunTime();
            }
        }

        /// <summary>
        /// Gets the number of times the registered task has missed a scheduled run.
        /// </summary>
        public int NumberOfMissedRuns
        {
            get
            {
                if (v2Task != null)
                    return v2Task.NumberOfMissedRuns;

                throw new NotV1SupportedException();
            }
        }

        /// <summary>
        /// Gets the path to where the registered task is stored.
        /// </summary>
        public string Path
        {
            get
            {
                if (v2Task != null)
                    return v2Task.Path;
                return GetV1Path(v1Task);
            }
        }

		/// <summary>
		/// Gets or sets the security descriptor for the task.
		/// </summary>
		/// <value>The security descriptor.</value>
		public System.Security.AccessControl.GenericSecurityDescriptor SecurityDescriptor
		{
			get
			{
				string sddl = GetSecurityDescriptorSddlForm(System.Security.AccessControl.AccessControlSections.All);
				return new System.Security.AccessControl.RawSecurityDescriptor(sddl);
			}
			set
			{
				SetSecurityDescriptorSddlForm(value.GetSddlForm(System.Security.AccessControl.AccessControlSections.All), System.Security.AccessControl.AccessControlSections.All);
			}
		}

        /// <summary>
        /// Gets the operational state of the registered task.
        /// </summary>
        public TaskState State
        {
            get
            {
                if (v2Task != null)
                    return v2Task.State;

                switch (v1Task.GetStatus())
                {
                    case V1Interop.TaskStatus.Ready:
                    case V1Interop.TaskStatus.NeverRun:
                    case V1Interop.TaskStatus.NoMoreRuns:
                    case V1Interop.TaskStatus.Terminated:
                        return TaskState.Ready;
                    case V1Interop.TaskStatus.Running:
                        return TaskState.Running;
                    case V1Interop.TaskStatus.Disabled:
                        return TaskState.Disabled;
                    case V1Interop.TaskStatus.NotScheduled:
                    case V1Interop.TaskStatus.NoTriggers:
                    case V1Interop.TaskStatus.NoTriggerTime:
                    default:
                        return TaskState.Unknown;
                }
            }
        }

        /// <summary>
        /// Gets or sets the <see cref="TaskService"/> that manages this task.
        /// </summary>
        /// <value>The task service.</value>
        public TaskService TaskService
        {
            get; private set;
        }

        /// <summary>
        /// Gets the XML-formatted registration information for the registered task. Not available with Task Scheduler 1.0.
        /// </summary>
        public string Xml
        {
            get
            {
                if (v2Task != null)
                    return v2Task.Xml;

                throw new NotV1SupportedException();
            }
        }

        /// <summary>
        /// Releases all resources used by this class.
        /// </summary>
        public void Dispose()
        {
            if (v2Task != null)
                Marshal.ReleaseComObject(v2Task);
            v1Task = null;
        }

        /// <summary>
        /// Gets the times that the registered task is scheduled to run during a specified time.
		/// </summary>
        /// <param name="start">The starting time for the query.</param>
        /// <param name="end">The ending time for the query.</param>
		/// <param name="count">The requested number of runs. A value of 0 will return all times requested.</param>
        /// <returns>The scheduled times that the task will run.</returns>
        public DateTime[] GetRunTimes(DateTime start, DateTime end, uint count = 0)
        {
			const ushort TASK_MAX_RUN_TIMES = 1440;

			TaskScheduler.V1Interop.SystemTime stStart = new TaskScheduler.V1Interop.SystemTime(start);
            TaskScheduler.V1Interop.SystemTime stEnd = new TaskScheduler.V1Interop.SystemTime(end);
            IntPtr runTimes = IntPtr.Zero, st;
			if (v2Task != null)
				v2Task.GetRunTimes(stStart, stEnd, ref count, ref runTimes);
			else
			{
				ushort count1 = (count > 0 && count <= TASK_MAX_RUN_TIMES) ? (ushort)count : TASK_MAX_RUN_TIMES;
				v1Task.GetRunTimes(ref stStart, ref stEnd, ref count1, ref runTimes);
				count = count1;
			}
            DateTime[] ret = new DateTime[count];
            for (int i = 0; i < count; i++)
            {
				st = new IntPtr(runTimes.ToInt64() + (i * Marshal.SizeOf(typeof(TaskScheduler.V1Interop.SystemTime))));
                ret[i] = (TaskScheduler.V1Interop.SystemTime)Marshal.PtrToStructure(st, typeof(TaskScheduler.V1Interop.SystemTime));
            }
            Marshal.FreeCoTaskMem(runTimes);
            return ret;
        }

        /// <summary>
        /// Gets the security descriptor for the task. Not available to Task Scheduler 1.0.
        /// </summary>
        /// <param name="includeSections">Section(s) of the security descriptor to return.</param>
        /// <returns>The security descriptor for the task.</returns>
        public string GetSecurityDescriptorSddlForm(System.Security.AccessControl.AccessControlSections includeSections)
        {
            if (v2Task != null)
                return v2Task.GetSecurityDescriptor((int)includeSections);

            throw new NotV1SupportedException();
        }

		/// <summary>
		/// Updates the task with any changes made to the <see cref="Definition"/> by calling <see cref="TaskFolder.RegisterTaskDefinition(string, TaskDefinition)"/> from the currently registered folder using the currently registered name.
		/// </summary>
		/// <exception cref="System.Security.SecurityException">Thrown if task was previously registered with a password.</exception>
		public void RegisterChanges()
		{
			if (this.Definition.Principal.LogonType == TaskLogonType.InteractiveTokenOrPassword || this.Definition.Principal.LogonType == TaskLogonType.Password || this.Definition.Principal.LogonType == TaskLogonType.Group)
				throw new System.Security.SecurityException("Tasks which have been registered previously with stored passwords must use the TaskFolder.RegisterTaskDefinition method for updates.");
			TaskService.GetFolder(System.IO.Path.GetDirectoryName(this.Path)).RegisterTaskDefinition(this.Name, this.Definition);
		}

        /// <summary>
        /// Runs the registered task immediately.
        /// </summary>
        /// <param name="parameters">The parameters used as values in the task actions.</param>
        /// <returns>A <see cref="RunningTask"/> instance that defines the new instance of the task.</returns>
        public RunningTask Run(params string[] parameters)
        {
			if (v2Task != null)
			{
				if (parameters != null && parameters.Length > 32)
					throw new ArgumentOutOfRangeException("parameters", "A maximum of 32 values is allowed.");
				return new RunningTask(this.TaskService, this.v2Task, v2Task.Run(parameters));
			}

            v1Task.Run();
            return new RunningTask(this.TaskService, this.v1Task);
        }

        /// <summary>
        /// Runs the registered task immediately using specified flags and a session identifier.
        /// </summary>
        /// <param name="flags">Defines how the task is run.</param>
        /// <param name="sessionID">The terminal server session in which you want to start the task.</param>
        /// <param name="user">The user for which the task runs.</param>
        /// <param name="parameters">The parameters used as values in the task actions.</param>
        /// <returns>A <see cref="RunningTask"/> instance that defines the new instance of the task.</returns>
        public RunningTask RunEx(TaskRunFlags flags, int sessionID, string user, params string[] parameters)
        {
            if (v2Task != null)
                return new RunningTask(this.TaskService, this.v2Task, v2Task.RunEx(parameters, (int)flags, sessionID, user));
            throw new NotV1SupportedException();
        }

        /// <summary>
        /// Sets the security descriptor for the task. Not available to Task Scheduler 1.0.
        /// </summary>
        /// <param name="sddlForm">The security descriptor for the task.</param>
        /// <param name="includeSections">Section(s) of the security descriptor to set.</param>
        public void SetSecurityDescriptorSddlForm(string sddlForm, System.Security.AccessControl.AccessControlSections includeSections)
        {
            if (v2Task != null)
                v2Task.SetSecurityDescriptor(sddlForm, (int)includeSections);

            throw new NotV1SupportedException();
        }

        /// <summary>
        /// Shows the property page for the task (v1.0 only).
        /// </summary>
        public void ShowPropertyPage()
        {
            if (v1Task != null)
                v1Task.EditWorkItem(IntPtr.Zero, 0);
            else
                throw new NotV2SupportedException();
        }

        /// <summary>
        /// Stops the registered task immediately.
        /// </summary>
        public void Stop()
        {
            if (v2Task != null)
                v2Task.Stop(0);
			else
	            v1Task.Terminate();
        }
{'fraction_non_alphanumeric': 0.08487645042986833, 'fraction_numerical': 0.013016292911681597, 'mean_word_length': 1.8465751080306336, 'pattern_counts': {'":': 0, '<': 530, '<?xml version=': 0, '>': 529, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5942697', 'n_tokens_mistral': 17604, 'n_tokens_neox': 16426, 'n_words': 5709}