using System; using System.Runtime.InteropServices; using JetBrains.Annotations; // ReSharper disable InconsistentNaming // ReSharper disable FieldCanBeMadeReadOnly.Global namespace Microsoft.Win32.TaskScheduler.V1Interop { #pragma warning disable CS0618 // Type or member is obsolete #region class HRESULT -- Values peculiar to the task scheduler. internal class HResult { // The task is ready to run at its next scheduled time. public const int SCHED_S_TASK_READY = 0x00041300; // The task is currently running. public const int SCHED_S_TASK_RUNNING = 0x00041301; // The task will not run at the scheduled times because it has been disabled. public const int SCHED_S_TASK_DISABLED = 0x00041302; // The task has not yet run. public const int SCHED_S_TASK_HAS_NOT_RUN = 0x00041303; // There are no more runs scheduled for this task. public const int SCHED_S_TASK_NO_MORE_RUNS = 0x00041304; // One or more of the properties that are needed to run this task on a schedule have not been set. public const int SCHED_S_TASK_NOT_SCHEDULED = 0x00041305; // The last run of the task was terminated by the user. public const int SCHED_S_TASK_TERMINATED = 0x00041306; // Either the task has no triggers or the existing triggers are disabled or not set. public const int SCHED_S_TASK_NO_VALID_TRIGGERS = 0x00041307; // Event triggers don't have set run times. public const int SCHED_S_EVENT_TRIGGER = 0x00041308; // Trigger not found. public const int SCHED_E_TRIGGER_NOT_FOUND = unchecked((int)0x80041309); // One or more of the properties that are needed to run this task have not been set. public const int SCHED_E_TASK_NOT_READY = unchecked((int)0x8004130A); // There is no running instance of the task to terminate. public const int SCHED_E_TASK_NOT_RUNNING = unchecked((int)0x8004130B); // The Task Scheduler Service is not installed on this computer. public const int SCHED_E_SERVICE_NOT_INSTALLED = unchecked((int)0x8004130C); // The task object could not be opened. public const int SCHED_E_CANNOT_OPEN_TASK = unchecked((int)0x8004130D); // The object is either an invalid task object or is not a task object. public const int SCHED_E_INVALID_TASK = unchecked((int)0x8004130E); // No account information could be found in the Task Scheduler security database for the task indicated. public const int SCHED_E_ACCOUNT_INFORMATION_NOT_SET = unchecked((int)0x8004130F); // Unable to establish existence of the account specified. public const int SCHED_E_ACCOUNT_NAME_NOT_FOUND = unchecked((int)0x80041310); // Corruption was detected in the Task Scheduler security database; the database has been reset. public const int SCHED_E_ACCOUNT_DBASE_CORRUPT = unchecked((int)0x80041311); // Task Scheduler security services are available only on Windows NT. public const int SCHED_E_NO_SECURITY_SERVICES = unchecked((int)0x80041312); // The task object version is either unsupported or invalid. public const int SCHED_E_UNKNOWN_OBJECT_VERSION = unchecked((int)0x80041313); // The task has been configured with an unsupported combination of account settings and run time options. public const int SCHED_E_UNSUPPORTED_ACCOUNT_OPTION = unchecked((int)0x80041314); // The Task Scheduler Service is not running. public const int SCHED_E_SERVICE_NOT_RUNNING = unchecked((int)0x80041315); // The Task Scheduler service must be configured to run in the System account to function properly. Individual tasks may be configured to run in other accounts. public const int SCHED_E_SERVICE_NOT_LOCALSYSTEM = unchecked((int)0x80041316); } #endregion #region Enums /// /// Options for a task, used for the Flags property of a Task. Uses the /// "Flags" attribute, so these values are combined with |. /// Some flags are documented as Windows 95 only, but they have a /// user interface in Windows XP so that may not be true. /// [Flags] internal enum TaskFlags { /// /// The interactive flag is set if the task is intended to be displayed to the user. /// If the flag is not set, no user interface associated with the task is presented /// to the user when the task is executed. /// Interactive = 0x1, /// /// The task will be deleted when there are no more scheduled run times. /// DeleteWhenDone = 0x2, /// /// The task is disabled. This is useful to temporarily prevent a task from running /// at the scheduled time(s). /// Disabled = 0x4, /// /// The task begins only if the computer is not in use at the scheduled start time. Windows 95 only. /// StartOnlyIfIdle = 0x10, /// /// The task terminates if the computer makes an idle to non-idle transition while the task is running. /// The computer is not considered idle until the IdleWait triggers' time elapses with no user input. /// Windows 95 only. For information regarding idle triggers, see . /// KillOnIdleEnd = 0x20, /// /// The task does not start if its target computer is running on battery power. Windows 95 only. /// DontStartIfOnBatteries = 0x40, /// /// The task ends, and the associated application quits if the task's target computer switches /// to battery power. Windows 95 only. /// KillIfGoingOnBatteries = 0x80, /// /// The task runs only if the system is docked. Windows 95 only. /// RunOnlyIfDocked = 0x100, /// /// The work item created will be hidden. /// Hidden = 0x200, /// /// The task runs only if there is currently a valid Internet connection. /// This feature is currently not implemented. /// RunIfConnectedToInternet = 0x400, /// /// The task starts again if the computer makes a non-idle to idle transition before all the /// task's task_triggers elapse. (Use this flag in conjunction with KillOnIdleEnd.) Windows 95 only. /// RestartOnIdleResume = 0x800, /// /// The task runs only if the SYSTEM account is available. /// SystemRequired = 0x1000, /// /// The task runs only if the user specified in SetAccountInformation is logged on interactively. /// This flag has no effect on work items set to run in the local account. /// RunOnlyIfLoggedOn = 0x2000 } /// /// Status values returned for a task. Some values have been determined to occur although /// they do no appear in the Task Scheduler system documentation. /// internal enum TaskStatus { /// The task is ready to run at its next scheduled time. Ready = HResult.SCHED_S_TASK_READY, /// The task is currently running. Running = HResult.SCHED_S_TASK_RUNNING, /// One or more of the properties that are needed to run this task on a schedule have not been set. NotScheduled = HResult.SCHED_S_TASK_NOT_SCHEDULED, /// The task has not yet run. NeverRun = HResult.SCHED_S_TASK_HAS_NOT_RUN, /// The task will not run at the scheduled times because it has been disabled. Disabled = HResult.SCHED_S_TASK_DISABLED, /// There are no more runs scheduled for this task. NoMoreRuns = HResult.SCHED_S_TASK_NO_MORE_RUNS, /// The last run of the task was terminated by the user. Terminated = HResult.SCHED_S_TASK_TERMINATED, /// Either the task has no triggers or the existing triggers are disabled or not set. NoTriggers = HResult.SCHED_S_TASK_NO_VALID_TRIGGERS, /// Event triggers don't have set run times. NoTriggerTime = HResult.SCHED_S_EVENT_TRIGGER } /// Valid types of triggers internal enum TaskTriggerType { /// Trigger is set to run the task a single time. RunOnce = 0, /// Trigger is set to run the task on a daily interval. RunDaily = 1, /// Trigger is set to run the work item on specific days of a specific week of a specific month. RunWeekly = 2, /// Trigger is set to run the task on a specific day(s) of the month. RunMonthly = 3, /// Trigger is set to run the task on specific days, weeks, and months. RunMonthlyDOW = 4, /// Trigger is set to run the task if the system remains idle for the amount of time specified by the idle wait time of the task. OnIdle = 5, /// Trigger is set to run the task at system startup. OnSystemStart = 6, /// Trigger is set to run the task when a user logs on. OnLogon = 7 } [Flags] internal enum TaskTriggerFlags : uint { HasEndDate = 0x1, KillAtDurationEnd = 0x2, Disabled = 0x4 } #endregion #region Structs [StructLayout(LayoutKind.Sequential)] internal struct Daily { public ushort DaysInterval; } [StructLayout(LayoutKind.Sequential)] internal struct Weekly { public ushort WeeksInterval; public DaysOfTheWeek DaysOfTheWeek; } [StructLayout(LayoutKind.Sequential)] internal struct MonthlyDate { public uint Days; public MonthsOfTheYear Months; } [StructLayout(LayoutKind.Sequential)] internal struct MonthlyDOW { public ushort WhichWeek; public DaysOfTheWeek DaysOfTheWeek; public MonthsOfTheYear Months; public WhichWeek V2WhichWeek { get { return (WhichWeek)(1 << ((short)WhichWeek - 1)); } set { int idx = Array.IndexOf(new short[] { 0x1, 0x2, 0x4, 0x8, 0x10 }, (short)value); if (idx >= 0) WhichWeek = (ushort)(idx + 1); else throw new NotV1SupportedException("Only a single week can be set with Task Scheduler 1.0."); } } } [StructLayout(LayoutKind.Explicit)] internal struct TriggerTypeData { [FieldOffset(0)] public Daily daily; [FieldOffset(0)] public Weekly weekly; [FieldOffset(0)] public MonthlyDate monthlyDate; [FieldOffset(0)] public MonthlyDOW monthlyDOW; } [StructLayout(LayoutKind.Sequential)] internal struct TaskTrigger { public ushort TriggerSize; // Structure size. public ushort Reserved1; // Reserved. Must be zero. public ushort BeginYear; // Trigger beginning date year. public ushort BeginMonth; // Trigger beginning date month. public ushort BeginDay; // Trigger beginning date day. public ushort EndYear; // Optional trigger ending date year. public ushort EndMonth; // Optional trigger ending date month. public ushort EndDay; // Optional trigger ending date day. public ushort StartHour; // Run bracket start time hour. public ushort StartMinute; // Run bracket start time minute. public uint MinutesDuration; // Duration of run bracket. public uint MinutesInterval; // Run bracket repetition interval. public TaskTriggerFlags Flags; // Trigger flags. public TaskTriggerType Type; // Trigger type. public TriggerTypeData Data; // Trigger data peculiar to this type (union). public ushort Reserved2; // Reserved. Must be zero. public ushort RandomMinutesInterval; // Maximum number of random minutes after start time. public DateTime BeginDate { get { try { return BeginYear == 0 ? DateTime.MinValue : new DateTime(BeginYear, BeginMonth, BeginDay, StartHour, StartMinute, 0, DateTimeKind.Unspecified); } catch { return DateTime.MinValue; } } set { if (value != DateTime.MinValue) { DateTime local = value.Kind == DateTimeKind.Utc ? value.ToLocalTime() : value; BeginYear = (ushort)local.Year; BeginMonth = (ushort)local.Month; BeginDay = (ushort)local.Day; StartHour = (ushort)local.Hour; StartMinute = (ushort)local.Minute; } else BeginYear = BeginMonth = BeginDay = StartHour = StartMinute = 0; } } public DateTime? EndDate { get { try { return EndYear == 0 ? (DateTime?)null : new DateTime(EndYear, EndMonth, EndDay); } catch { return DateTime.MaxValue; } } set { if (value.HasValue) { EndYear = (ushort)value.Value.Year; EndMonth = (ushort)value.Value.Month; EndDay = (ushort)value.Value.Day; Flags |= TaskTriggerFlags.HasEndDate; } else { EndYear = EndMonth = EndDay = 0; Flags &= ~TaskTriggerFlags.HasEndDate; } } } public override string ToString() => $"Trigger Type: {Type};\n> Start: {BeginDate}; End: {(EndYear == 0 ? "null" : EndDate?.ToString())};\n> DurMin: {MinutesDuration}; DurItv: {MinutesInterval};\n>"; } #endregion [ComImport, Guid("148BD527-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity, CoClass(typeof(CTaskScheduler))] internal interface ITaskScheduler { void SetTargetComputer([In, MarshalAs(UnmanagedType.LPWStr)] string Computer); CoTaskMemString GetTargetComputer(); [return: MarshalAs(UnmanagedType.Interface)] IEnumWorkItems Enum(); [return: MarshalAs(UnmanagedType.Interface)] ITask Activate([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string Name, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid); void Delete([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string Name); [return: MarshalAs(UnmanagedType.Interface)] ITask NewWorkItem([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string TaskName, [In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid); void AddWorkItem([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string TaskName, [In, MarshalAs(UnmanagedType.Interface)] ITask WorkItem); void IsOfType([In, MarshalAs(UnmanagedType.LPWStr)][NotNull] string TaskName, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid); } [Guid("148BD528-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity] internal interface IEnumWorkItems { [PreserveSig()] //int Next([In] uint celt, [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 0)] out string[] rgpwszNames, [Out] out uint pceltFetched); int Next([In] uint RequestCount, [Out] out IntPtr Names, [Out] out uint Fetched); void Skip([In] uint Count); void Reset(); [return: MarshalAs(UnmanagedType.Interface)] IEnumWorkItems Clone(); } #if WorkItem // The IScheduledWorkItem interface is actually never used because ITask inherits all of its // methods. As ITask is the only kind of WorkItem (in 2002) it is the only interface we need. [Guid("a6b952f0-a4b1-11d0-997d-00aa006887ec"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IScheduledWorkItem { void CreateTrigger([Out] out ushort NewTriggerIndex, [Out, MarshalAs(UnmanagedType.Interface)] out ITaskTrigger Trigger); void DeleteTrigger([In] ushort TriggerIndex); void GetTriggerCount([Out] out ushort Count); void GetTrigger([In] ushort TriggerIndex, [Out, MarshalAs(UnmanagedType.Interface)] out ITaskTrigger Trigger); void GetTriggerString([In] ushort TriggerIndex, out System.IntPtr TriggerString); void GetRunTimes([In, MarshalAs(UnmanagedType.Struct)] SystemTime Begin, [In, MarshalAs(UnmanagedType.Struct)] SystemTime End, ref ushort Count, [Out] out System.IntPtr TaskTimes); void GetNextRunTime([In, Out, MarshalAs(UnmanagedType.Struct)] ref SystemTime NextRun); void SetIdleWait([In] ushort IdleMinutes, [In] ushort DeadlineMinutes); void GetIdleWait([Out] out ushort IdleMinutes, [Out] out ushort DeadlineMinutes); void Run(); void Terminate(); void EditWorkItem([In] uint hParent, [In] uint dwReserved); void GetMostRecentRunTime([In, Out, MarshalAs(UnmanagedType.Struct)] ref SystemTime LastRun); void GetStatus([Out, MarshalAs(UnmanagedType.Error)] out int Status); void GetExitCode([Out] out uint ExitCode); void SetComment([In, MarshalAs(UnmanagedType.LPWStr)] string Comment); void GetComment(out System.IntPtr Comment); void SetCreator([In, MarshalAs(UnmanagedType.LPWStr)] string Creator); void GetCreator(out System.IntPtr Creator); void SetWorkItemData([In] ushort DataLen, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0, ArraySubType=UnmanagedType.U1)] byte[] Data); void GetWorkItemData([Out] out ushort DataLen, [Out] out System.IntPtr Data); void SetErrorRetryCount([In] ushort RetryCount); void GetErrorRetryCount([Out] out ushort RetryCount); void SetErrorRetryInterval([In] ushort RetryInterval); void GetErrorRetryInterval([Out] out ushort RetryInterval); void SetFlags([In] uint Flags); void GetFlags([Out] out uint Flags); void SetAccountInformation([In, MarshalAs(UnmanagedType.LPWStr)] string AccountName, [In, MarshalAs(UnmanagedType.LPWStr)] string Password); void GetAccountInformation(out System.IntPtr AccountName); } #endif [ComImport, Guid("148BD524-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity, CoClass(typeof(CTask))] internal interface ITask { [return: MarshalAs(UnmanagedType.Interface)] ITaskTrigger CreateTrigger([Out] out ushort NewTriggerIndex); void DeleteTrigger([In] ushort TriggerIndex); [return: MarshalAs(UnmanagedType.U2)] ushort GetTriggerCount(); [return: MarshalAs(UnmanagedType.Interface)] ITaskTrigger GetTrigger([In] ushort TriggerIndex); CoTaskMemString GetTriggerString([In] ushort TriggerIndex); void GetRunTimes([In, MarshalAs(UnmanagedType.Struct)] ref NativeMethods.SYSTEMTIME Begin, [In, MarshalAs(UnmanagedType.Struct)] ref NativeMethods.SYSTEMTIME End, ref ushort Count, [In, Out] ref IntPtr TaskTimes); [return: MarshalAs(UnmanagedType.Struct)] NativeMethods.SYSTEMTIME GetNextRunTime(); void SetIdleWait([In] ushort IdleMinutes, [In] ushort DeadlineMinutes); void GetIdleWait([Out] out ushort IdleMinutes, [Out] out ushort DeadlineMinutes); void Run(); void Terminate(); void EditWorkItem([In] IntPtr hParent, [In] uint dwReserved); [return: MarshalAs(UnmanagedType.Struct)] NativeMethods.SYSTEMTIME GetMostRecentRunTime(); TaskStatus GetStatus(); uint GetExitCode(); void SetComment([In, MarshalAs(UnmanagedType.LPWStr)] string Comment); CoTaskMemString GetComment(); void SetCreator([In, MarshalAs(UnmanagedType.LPWStr)] string Creator); CoTaskMemString GetCreator(); void SetWorkItemData([In] ushort DataLen, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.U1)] byte[] Data); void GetWorkItemData(out ushort DataLen, [Out] out IntPtr Data); void SetErrorRetryCount([In] ushort RetryCount); ushort GetErrorRetryCount(); void SetErrorRetryInterval([In] ushort RetryInterval); ushort GetErrorRetryInterval(); void SetFlags([In] TaskFlags Flags); TaskFlags GetFlags(); void SetAccountInformation([In, MarshalAs(UnmanagedType.LPWStr)] string AccountName, [In] IntPtr Password); CoTaskMemString GetAccountInformation(); void SetApplicationName([In, MarshalAs(UnmanagedType.LPWStr)] string ApplicationName); CoTaskMemString GetApplicationName(); void SetParameters([In, MarshalAs(UnmanagedType.LPWStr)] string Parameters); CoTaskMemString GetParameters(); void SetWorkingDirectory([In, MarshalAs(UnmanagedType.LPWStr)] string WorkingDirectory); CoTaskMemString GetWorkingDirectory(); void SetPriority([In] uint Priority); uint GetPriority(); void SetTaskFlags([In] uint Flags); uint GetTaskFlags(); void SetMaxRunTime([In] uint MaxRunTimeMS); uint GetMaxRunTime(); } [Guid("148BD52B-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), System.Security.SuppressUnmanagedCodeSecurity] internal interface ITaskTrigger { void SetTrigger([In, Out, MarshalAs(UnmanagedType.Struct)] ref TaskTrigger Trigger); [return: MarshalAs(UnmanagedType.Struct)] TaskTrigger GetTrigger(); CoTaskMemString GetTriggerString(); } [ComImport, Guid("148BD52A-A2AB-11CE-B11F-00AA00530503"), System.Security.SuppressUnmanagedCodeSecurity, ClassInterface(ClassInterfaceType.None)] internal class CTaskScheduler { } [ComImport, Guid("148BD520-A2AB-11CE-B11F-00AA00530503"), System.Security.SuppressUnmanagedCodeSecurity, ClassInterface(ClassInterfaceType.None)] internal class CTask { } internal sealed class CoTaskMemString : SafeHandle { public CoTaskMemString() : base(IntPtr.Zero, true) { } public CoTaskMemString(IntPtr handle) : this() { SetHandle(handle); } public CoTaskMemString(string text) : this() { SetHandle(Marshal.StringToCoTaskMemUni(text)); } public static implicit operator string (CoTaskMemString cmem) => cmem.ToString(); public override bool IsInvalid => handle == IntPtr.Zero; protected override bool ReleaseHandle() { Marshal.FreeCoTaskMem(handle); return true; } public override string ToString() => Marshal.PtrToStringUni(handle); } }