962 lines
37 KiB
C#
962 lines
37 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.Management.Automation;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.AccessControl;
|
|
using System.Text;
|
|
|
|
using TaskScheduler;
|
|
|
|
namespace Microsoft.PowerShell.ScheduledJob
|
|
{
|
|
/// <summary>
|
|
/// Managed code class to provide Windows Task Scheduler functionality for
|
|
/// scheduled jobs.
|
|
/// </summary>
|
|
internal sealed class ScheduledJobWTS : IDisposable
|
|
{
|
|
#region Private Members
|
|
|
|
private ITaskService _taskScheduler;
|
|
private ITaskFolder _iRootFolder;
|
|
|
|
private const short WTSSunday = 0x01;
|
|
private const short WTSMonday = 0x02;
|
|
private const short WTSTuesday = 0x04;
|
|
private const short WTSWednesday = 0x08;
|
|
private const short WTSThursday = 0x10;
|
|
private const short WTSFriday = 0x20;
|
|
private const short WTSSaturday = 0x40;
|
|
|
|
// Task Scheduler folders for PowerShell scheduled job tasks.
|
|
private const string TaskSchedulerWindowsFolder = @"\Microsoft\Windows";
|
|
private const string ScheduledJobSubFolder = @"PowerShell\ScheduledJobs";
|
|
private const string ScheduledJobTasksRootFolder = @"\Microsoft\Windows\PowerShell\ScheduledJobs";
|
|
|
|
// Define a single Action Id since PowerShell Scheduled Job tasks will have only one action.
|
|
private const string ScheduledJobTaskActionId = "StartPowerShellJob";
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
|
|
public ScheduledJobWTS()
|
|
{
|
|
// Create the Windows Task Scheduler object.
|
|
_taskScheduler = (ITaskService)new TaskScheduler.TaskScheduler();
|
|
|
|
// Connect the task scheduler object to the local machine
|
|
// using the current user security token.
|
|
_taskScheduler.Connect(null, null, null, null);
|
|
|
|
// Get or create the root folder in Task Scheduler for PowerShell scheduled jobs.
|
|
_iRootFolder = GetRootFolder();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// Retrieves job triggers from WTS with provided task Id.
|
|
/// </summary>
|
|
/// <param name="taskId">Task Id.</param>
|
|
/// <exception cref="ScheduledJobException">Task not found.</exception>
|
|
/// <returns>ScheduledJobTriggers.</returns>
|
|
public Collection<ScheduledJobTrigger> GetJobTriggers(
|
|
string taskId)
|
|
{
|
|
if (string.IsNullOrEmpty(taskId))
|
|
{
|
|
throw new PSArgumentException("taskId");
|
|
}
|
|
|
|
ITaskDefinition iTaskDefinition = FindTask(taskId);
|
|
|
|
Collection<ScheduledJobTrigger> jobTriggers = new Collection<ScheduledJobTrigger>();
|
|
ITriggerCollection iTriggerCollection = iTaskDefinition.Triggers;
|
|
if (iTriggerCollection != null)
|
|
{
|
|
foreach (ITrigger iTrigger in iTriggerCollection)
|
|
{
|
|
ScheduledJobTrigger jobTrigger = CreateJobTrigger(iTrigger);
|
|
if (jobTrigger == null)
|
|
{
|
|
string msg = StringUtil.Format(ScheduledJobErrorStrings.UnknownTriggerType, taskId, iTrigger.Id);
|
|
throw new ScheduledJobException(msg);
|
|
}
|
|
|
|
jobTriggers.Add(jobTrigger);
|
|
}
|
|
}
|
|
|
|
return jobTriggers;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves options for the provided task Id.
|
|
/// </summary>
|
|
/// <param name="taskId">Task Id.</param>
|
|
/// <exception cref="ScheduledJobException">Task not found.</exception>
|
|
/// <returns>ScheduledJobOptions.</returns>
|
|
public ScheduledJobOptions GetJobOptions(
|
|
string taskId)
|
|
{
|
|
if (string.IsNullOrEmpty(taskId))
|
|
{
|
|
throw new PSArgumentException("taskId");
|
|
}
|
|
|
|
ITaskDefinition iTaskDefinition = FindTask(taskId);
|
|
|
|
return CreateJobOptions(iTaskDefinition);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a boolean indicating whether the job/task is enabled
|
|
/// in the Task Scheduler.
|
|
/// </summary>
|
|
/// <param name="taskId"></param>
|
|
/// <returns></returns>
|
|
public bool GetTaskEnabled(
|
|
string taskId)
|
|
{
|
|
if (string.IsNullOrEmpty(taskId))
|
|
{
|
|
throw new PSArgumentException("taskId");
|
|
}
|
|
|
|
ITaskDefinition iTaskDefinition = FindTask(taskId);
|
|
|
|
return iTaskDefinition.Settings.Enabled;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new task in WTS with information from ScheduledJobDefinition.
|
|
/// </summary>
|
|
/// <param name="definition">ScheduledJobDefinition.</param>
|
|
public void CreateTask(
|
|
ScheduledJobDefinition definition)
|
|
{
|
|
if (definition == null)
|
|
{
|
|
throw new PSArgumentNullException("definition");
|
|
}
|
|
|
|
// Create task definition
|
|
ITaskDefinition iTaskDefinition = _taskScheduler.NewTask(0);
|
|
|
|
// Add task options.
|
|
AddTaskOptions(iTaskDefinition, definition.Options);
|
|
|
|
// Add task triggers.
|
|
foreach (ScheduledJobTrigger jobTrigger in definition.JobTriggers)
|
|
{
|
|
AddTaskTrigger(iTaskDefinition, jobTrigger);
|
|
}
|
|
|
|
// Add task action.
|
|
AddTaskAction(iTaskDefinition, definition);
|
|
|
|
// Create a security descriptor for the current user so that only the user
|
|
// (and Local System account) can see/access the registered task.
|
|
string startSddl = "D:P(A;;GA;;;SY)(A;;GA;;;BA)"; // DACL Allow Generic Access to System and BUILTIN\Administrators.
|
|
System.Security.Principal.SecurityIdentifier userSid =
|
|
System.Security.Principal.WindowsIdentity.GetCurrent().User;
|
|
CommonSecurityDescriptor SDesc = new CommonSecurityDescriptor(false, false, startSddl);
|
|
SDesc.DiscretionaryAcl.AddAccess(AccessControlType.Allow, userSid, 0x10000000, InheritanceFlags.None, PropagationFlags.None);
|
|
string sddl = SDesc.GetSddlForm(AccessControlSections.All);
|
|
|
|
// Register this new task with the Task Scheduler.
|
|
if (definition.Credential == null)
|
|
{
|
|
// Register task to run as currently logged on user.
|
|
_iRootFolder.RegisterTaskDefinition(
|
|
definition.Name,
|
|
iTaskDefinition,
|
|
(int)_TASK_CREATION.TASK_CREATE,
|
|
null, // User name
|
|
null, // Password
|
|
_TASK_LOGON_TYPE.TASK_LOGON_S4U,
|
|
sddl);
|
|
}
|
|
else
|
|
{
|
|
// Register task to run under provided user account/credentials.
|
|
_iRootFolder.RegisterTaskDefinition(
|
|
definition.Name,
|
|
iTaskDefinition,
|
|
(int)_TASK_CREATION.TASK_CREATE,
|
|
definition.Credential.UserName,
|
|
GetCredentialPassword(definition.Credential),
|
|
_TASK_LOGON_TYPE.TASK_LOGON_PASSWORD,
|
|
sddl);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes the WTS task for this ScheduledJobDefinition.
|
|
/// Throws error if one or more instances of this task are running.
|
|
/// Force parameter will stop all running instances and remove task.
|
|
/// </summary>
|
|
/// <param name="definition">ScheduledJobDefinition.</param>
|
|
/// <param name="force">Force running instances to stop and remove task.</param>
|
|
public void RemoveTask(
|
|
ScheduledJobDefinition definition,
|
|
bool force = false)
|
|
{
|
|
if (definition == null)
|
|
{
|
|
throw new PSArgumentNullException("definition");
|
|
}
|
|
|
|
RemoveTaskByName(definition.Name, force, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a Task Scheduler task from the PowerShell/ScheduledJobs folder
|
|
/// based on a task name.
|
|
/// </summary>
|
|
/// <param name="taskName">Task Scheduler task name.</param>
|
|
/// <param name="force">Force running instances to stop and remove task.</param>
|
|
/// <param name="firstCheckForTask">First check for existence of task.</param>
|
|
public void RemoveTaskByName(
|
|
string taskName,
|
|
bool force,
|
|
bool firstCheckForTask)
|
|
{
|
|
// Get registered task.
|
|
IRegisteredTask iRegisteredTask = null;
|
|
try
|
|
{
|
|
iRegisteredTask = _iRootFolder.GetTask(taskName);
|
|
}
|
|
catch (System.IO.DirectoryNotFoundException)
|
|
{
|
|
if (!firstCheckForTask)
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
catch (System.IO.FileNotFoundException)
|
|
{
|
|
if (!firstCheckForTask)
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
|
|
if (iRegisteredTask == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Check to see if any instances of this job/task is running.
|
|
IRunningTaskCollection iRunningTasks = iRegisteredTask.GetInstances(0);
|
|
if (iRunningTasks.Count > 0)
|
|
{
|
|
if (!force)
|
|
{
|
|
string msg = StringUtil.Format(ScheduledJobErrorStrings.CannotRemoveTaskRunningInstance, taskName);
|
|
throw new ScheduledJobException(msg);
|
|
}
|
|
|
|
// Stop all running tasks.
|
|
iRegisteredTask.Stop(0);
|
|
}
|
|
|
|
// Remove task.
|
|
_iRootFolder.DeleteTask(taskName, 0);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Starts task running from Task Scheduler.
|
|
/// </summary>
|
|
/// <param name="definition">ScheduledJobDefinition.</param>
|
|
/// <exception cref="System.IO.DirectoryNotFoundException"></exception>
|
|
/// <exception cref="System.IO.FileNotFoundException"></exception>
|
|
public void RunTask(
|
|
ScheduledJobDefinition definition)
|
|
{
|
|
// Get registered task.
|
|
IRegisteredTask iRegisteredTask = _iRootFolder.GetTask(definition.Name);
|
|
|
|
// Run task.
|
|
iRegisteredTask.Run(null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates an existing task in WTS with information from
|
|
/// ScheduledJobDefinition.
|
|
/// </summary>
|
|
/// <param name="definition">ScheduledJobDefinition.</param>
|
|
public void UpdateTask(
|
|
ScheduledJobDefinition definition)
|
|
{
|
|
if (definition == null)
|
|
{
|
|
throw new PSArgumentNullException("definition");
|
|
}
|
|
|
|
// Get task to update.
|
|
ITaskDefinition iTaskDefinition = FindTask(definition.Name);
|
|
|
|
// Replace options.
|
|
AddTaskOptions(iTaskDefinition, definition.Options);
|
|
|
|
// Set enabled state.
|
|
iTaskDefinition.Settings.Enabled = definition.Enabled;
|
|
|
|
// Replace triggers.
|
|
iTaskDefinition.Triggers.Clear();
|
|
foreach (ScheduledJobTrigger jobTrigger in definition.JobTriggers)
|
|
{
|
|
AddTaskTrigger(iTaskDefinition, jobTrigger);
|
|
}
|
|
|
|
// Replace action.
|
|
iTaskDefinition.Actions.Clear();
|
|
AddTaskAction(iTaskDefinition, definition);
|
|
|
|
// Register updated task.
|
|
if (definition.Credential == null)
|
|
{
|
|
// Register task to run as currently logged on user.
|
|
_iRootFolder.RegisterTaskDefinition(
|
|
definition.Name,
|
|
iTaskDefinition,
|
|
(int)_TASK_CREATION.TASK_UPDATE,
|
|
null, // User name
|
|
null, // Password
|
|
_TASK_LOGON_TYPE.TASK_LOGON_S4U,
|
|
null);
|
|
}
|
|
else
|
|
{
|
|
// Register task to run under provided user account/credentials.
|
|
_iRootFolder.RegisterTaskDefinition(
|
|
definition.Name,
|
|
iTaskDefinition,
|
|
(int)_TASK_CREATION.TASK_UPDATE,
|
|
definition.Credential.UserName,
|
|
GetCredentialPassword(definition.Credential),
|
|
_TASK_LOGON_TYPE.TASK_LOGON_PASSWORD,
|
|
null);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
|
|
/// <summary>
|
|
/// Creates a new WTS trigger based on the provided ScheduledJobTrigger object
|
|
/// and adds it to the provided ITaskDefinition object.
|
|
/// </summary>
|
|
/// <param name="iTaskDefinition">ITaskDefinition.</param>
|
|
/// <param name="jobTrigger">ScheduledJobTrigger.</param>
|
|
private void AddTaskTrigger(
|
|
ITaskDefinition iTaskDefinition,
|
|
ScheduledJobTrigger jobTrigger)
|
|
{
|
|
ITrigger iTrigger = null;
|
|
|
|
switch (jobTrigger.Frequency)
|
|
{
|
|
case TriggerFrequency.AtStartup:
|
|
{
|
|
iTrigger = iTaskDefinition.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_BOOT);
|
|
IBootTrigger iBootTrigger = iTrigger as IBootTrigger;
|
|
Debug.Assert(iBootTrigger != null);
|
|
|
|
iBootTrigger.Delay = ConvertTimeSpanToWTSString(jobTrigger.RandomDelay);
|
|
|
|
iTrigger.Id = jobTrigger.Id.ToString(CultureInfo.InvariantCulture);
|
|
iTrigger.Enabled = jobTrigger.Enabled;
|
|
}
|
|
|
|
break;
|
|
|
|
case TriggerFrequency.AtLogon:
|
|
{
|
|
iTrigger = iTaskDefinition.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_LOGON);
|
|
ILogonTrigger iLogonTrigger = iTrigger as ILogonTrigger;
|
|
Debug.Assert(iLogonTrigger != null);
|
|
|
|
iLogonTrigger.UserId = ScheduledJobTrigger.IsAllUsers(jobTrigger.User) ? null : jobTrigger.User;
|
|
iLogonTrigger.Delay = ConvertTimeSpanToWTSString(jobTrigger.RandomDelay);
|
|
|
|
iTrigger.Id = jobTrigger.Id.ToString(CultureInfo.InvariantCulture);
|
|
iTrigger.Enabled = jobTrigger.Enabled;
|
|
}
|
|
|
|
break;
|
|
|
|
case TriggerFrequency.Once:
|
|
{
|
|
iTrigger = iTaskDefinition.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_TIME);
|
|
ITimeTrigger iTimeTrigger = iTrigger as ITimeTrigger;
|
|
Debug.Assert(iTimeTrigger != null);
|
|
|
|
iTimeTrigger.RandomDelay = ConvertTimeSpanToWTSString(jobTrigger.RandomDelay);
|
|
|
|
// Time trigger repetition.
|
|
if (jobTrigger.RepetitionInterval != null &&
|
|
jobTrigger.RepetitionDuration != null)
|
|
{
|
|
iTimeTrigger.Repetition.Interval = ConvertTimeSpanToWTSString(jobTrigger.RepetitionInterval.Value);
|
|
if (jobTrigger.RepetitionDuration.Value == TimeSpan.MaxValue)
|
|
{
|
|
iTimeTrigger.Repetition.StopAtDurationEnd = false;
|
|
}
|
|
else
|
|
{
|
|
iTimeTrigger.Repetition.StopAtDurationEnd = true;
|
|
iTimeTrigger.Repetition.Duration = ConvertTimeSpanToWTSString(jobTrigger.RepetitionDuration.Value);
|
|
}
|
|
}
|
|
|
|
iTrigger.StartBoundary = ConvertDateTimeToString(jobTrigger.At);
|
|
iTrigger.Id = jobTrigger.Id.ToString(CultureInfo.InvariantCulture);
|
|
iTrigger.Enabled = jobTrigger.Enabled;
|
|
}
|
|
|
|
break;
|
|
|
|
case TriggerFrequency.Daily:
|
|
{
|
|
iTrigger = iTaskDefinition.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_DAILY);
|
|
IDailyTrigger iDailyTrigger = iTrigger as IDailyTrigger;
|
|
Debug.Assert(iDailyTrigger != null);
|
|
|
|
iDailyTrigger.RandomDelay = ConvertTimeSpanToWTSString(jobTrigger.RandomDelay);
|
|
iDailyTrigger.DaysInterval = (short)jobTrigger.Interval;
|
|
|
|
iTrigger.StartBoundary = ConvertDateTimeToString(jobTrigger.At);
|
|
iTrigger.Id = jobTrigger.Id.ToString(CultureInfo.InvariantCulture);
|
|
iTrigger.Enabled = jobTrigger.Enabled;
|
|
}
|
|
|
|
break;
|
|
|
|
case TriggerFrequency.Weekly:
|
|
{
|
|
iTrigger = iTaskDefinition.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_WEEKLY);
|
|
IWeeklyTrigger iWeeklyTrigger = iTrigger as IWeeklyTrigger;
|
|
Debug.Assert(iWeeklyTrigger != null);
|
|
|
|
iWeeklyTrigger.RandomDelay = ConvertTimeSpanToWTSString(jobTrigger.RandomDelay);
|
|
iWeeklyTrigger.WeeksInterval = (short)jobTrigger.Interval;
|
|
iWeeklyTrigger.DaysOfWeek = ConvertDaysOfWeekToMask(jobTrigger.DaysOfWeek);
|
|
|
|
iTrigger.StartBoundary = ConvertDateTimeToString(jobTrigger.At);
|
|
iTrigger.Id = jobTrigger.Id.ToString(CultureInfo.InvariantCulture);
|
|
iTrigger.Enabled = jobTrigger.Enabled;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a ScheduledJobTrigger object based on a provided WTS ITrigger.
|
|
/// </summary>
|
|
/// <param name="iTrigger">ITrigger.</param>
|
|
/// <returns>ScheduledJobTrigger.</returns>
|
|
private ScheduledJobTrigger CreateJobTrigger(
|
|
ITrigger iTrigger)
|
|
{
|
|
ScheduledJobTrigger rtnJobTrigger = null;
|
|
|
|
if (iTrigger is IBootTrigger)
|
|
{
|
|
IBootTrigger iBootTrigger = (IBootTrigger)iTrigger;
|
|
rtnJobTrigger = ScheduledJobTrigger.CreateAtStartupTrigger(
|
|
ParseWTSTime(iBootTrigger.Delay),
|
|
ConvertStringId(iBootTrigger.Id),
|
|
iBootTrigger.Enabled);
|
|
}
|
|
else if (iTrigger is ILogonTrigger)
|
|
{
|
|
ILogonTrigger iLogonTrigger = (ILogonTrigger)iTrigger;
|
|
rtnJobTrigger = ScheduledJobTrigger.CreateAtLogOnTrigger(
|
|
iLogonTrigger.UserId,
|
|
ParseWTSTime(iLogonTrigger.Delay),
|
|
ConvertStringId(iLogonTrigger.Id),
|
|
iLogonTrigger.Enabled);
|
|
}
|
|
else if (iTrigger is ITimeTrigger)
|
|
{
|
|
ITimeTrigger iTimeTrigger = (ITimeTrigger)iTrigger;
|
|
TimeSpan repInterval = ParseWTSTime(iTimeTrigger.Repetition.Interval);
|
|
TimeSpan repDuration = (repInterval != TimeSpan.Zero && iTimeTrigger.Repetition.StopAtDurationEnd == false) ?
|
|
TimeSpan.MaxValue : ParseWTSTime(iTimeTrigger.Repetition.Duration);
|
|
rtnJobTrigger = ScheduledJobTrigger.CreateOnceTrigger(
|
|
DateTime.Parse(iTimeTrigger.StartBoundary, CultureInfo.InvariantCulture),
|
|
ParseWTSTime(iTimeTrigger.RandomDelay),
|
|
repInterval,
|
|
repDuration,
|
|
ConvertStringId(iTimeTrigger.Id),
|
|
iTimeTrigger.Enabled);
|
|
}
|
|
else if (iTrigger is IDailyTrigger)
|
|
{
|
|
IDailyTrigger iDailyTrigger = (IDailyTrigger)iTrigger;
|
|
rtnJobTrigger = ScheduledJobTrigger.CreateDailyTrigger(
|
|
DateTime.Parse(iDailyTrigger.StartBoundary, CultureInfo.InvariantCulture),
|
|
(Int32)iDailyTrigger.DaysInterval,
|
|
ParseWTSTime(iDailyTrigger.RandomDelay),
|
|
ConvertStringId(iDailyTrigger.Id),
|
|
iDailyTrigger.Enabled);
|
|
}
|
|
else if (iTrigger is IWeeklyTrigger)
|
|
{
|
|
IWeeklyTrigger iWeeklyTrigger = (IWeeklyTrigger)iTrigger;
|
|
rtnJobTrigger = ScheduledJobTrigger.CreateWeeklyTrigger(
|
|
DateTime.Parse(iWeeklyTrigger.StartBoundary, CultureInfo.InvariantCulture),
|
|
(Int32)iWeeklyTrigger.WeeksInterval,
|
|
ConvertMaskToDaysOfWeekArray(iWeeklyTrigger.DaysOfWeek),
|
|
ParseWTSTime(iWeeklyTrigger.RandomDelay),
|
|
ConvertStringId(iWeeklyTrigger.Id),
|
|
iWeeklyTrigger.Enabled);
|
|
}
|
|
|
|
return rtnJobTrigger;
|
|
}
|
|
|
|
private void AddTaskOptions(
|
|
ITaskDefinition iTaskDefinition,
|
|
ScheduledJobOptions jobOptions)
|
|
{
|
|
iTaskDefinition.Settings.DisallowStartIfOnBatteries = !jobOptions.StartIfOnBatteries;
|
|
iTaskDefinition.Settings.StopIfGoingOnBatteries = jobOptions.StopIfGoingOnBatteries;
|
|
iTaskDefinition.Settings.WakeToRun = jobOptions.WakeToRun;
|
|
iTaskDefinition.Settings.RunOnlyIfIdle = !jobOptions.StartIfNotIdle;
|
|
iTaskDefinition.Settings.IdleSettings.StopOnIdleEnd = jobOptions.StopIfGoingOffIdle;
|
|
iTaskDefinition.Settings.IdleSettings.RestartOnIdle = jobOptions.RestartOnIdleResume;
|
|
iTaskDefinition.Settings.IdleSettings.IdleDuration = ConvertTimeSpanToWTSString(jobOptions.IdleDuration);
|
|
iTaskDefinition.Settings.IdleSettings.WaitTimeout = ConvertTimeSpanToWTSString(jobOptions.IdleTimeout);
|
|
iTaskDefinition.Settings.Hidden = !jobOptions.ShowInTaskScheduler;
|
|
iTaskDefinition.Settings.RunOnlyIfNetworkAvailable = !jobOptions.RunWithoutNetwork;
|
|
iTaskDefinition.Settings.AllowDemandStart = !jobOptions.DoNotAllowDemandStart;
|
|
iTaskDefinition.Settings.MultipleInstances = ConvertFromMultiInstances(jobOptions.MultipleInstancePolicy);
|
|
iTaskDefinition.Principal.RunLevel = (jobOptions.RunElevated) ?
|
|
_TASK_RUNLEVEL.TASK_RUNLEVEL_HIGHEST : _TASK_RUNLEVEL.TASK_RUNLEVEL_LUA;
|
|
}
|
|
|
|
private ScheduledJobOptions CreateJobOptions(
|
|
ITaskDefinition iTaskDefinition)
|
|
{
|
|
ITaskSettings iTaskSettings = iTaskDefinition.Settings;
|
|
IPrincipal iPrincipal = iTaskDefinition.Principal;
|
|
|
|
return new ScheduledJobOptions(
|
|
!iTaskSettings.DisallowStartIfOnBatteries,
|
|
iTaskSettings.StopIfGoingOnBatteries,
|
|
iTaskSettings.WakeToRun,
|
|
!iTaskSettings.RunOnlyIfIdle,
|
|
iTaskSettings.IdleSettings.StopOnIdleEnd,
|
|
iTaskSettings.IdleSettings.RestartOnIdle,
|
|
ParseWTSTime(iTaskSettings.IdleSettings.IdleDuration),
|
|
ParseWTSTime(iTaskSettings.IdleSettings.WaitTimeout),
|
|
!iTaskSettings.Hidden,
|
|
iPrincipal.RunLevel == _TASK_RUNLEVEL.TASK_RUNLEVEL_HIGHEST,
|
|
!iTaskSettings.RunOnlyIfNetworkAvailable,
|
|
!iTaskSettings.AllowDemandStart,
|
|
ConvertToMultiInstances(iTaskSettings));
|
|
}
|
|
|
|
private void AddTaskAction(
|
|
ITaskDefinition iTaskDefinition,
|
|
ScheduledJobDefinition definition)
|
|
{
|
|
IExecAction iExecAction = iTaskDefinition.Actions.Create(_TASK_ACTION_TYPE.TASK_ACTION_EXEC) as IExecAction;
|
|
Debug.Assert(iExecAction != null);
|
|
|
|
iExecAction.Id = ScheduledJobTaskActionId;
|
|
iExecAction.Path = definition.PSExecutionPath;
|
|
iExecAction.Arguments = definition.PSExecutionArgs;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets and returns the unsecured password for the provided
|
|
/// PSCredential object.
|
|
/// </summary>
|
|
/// <param name="credential">PSCredential.</param>
|
|
/// <returns>Unsecured password string.</returns>
|
|
private string GetCredentialPassword(PSCredential credential)
|
|
{
|
|
if (credential == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
IntPtr unmanagedString = IntPtr.Zero;
|
|
try
|
|
{
|
|
unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(credential.Password);
|
|
return Marshal.PtrToStringUni(unmanagedString);
|
|
}
|
|
finally
|
|
{
|
|
Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Utility Methods
|
|
|
|
/// <summary>
|
|
/// Gets the Task Scheduler root folder for Scheduled Jobs or
|
|
/// creates it if it does not exist.
|
|
/// </summary>
|
|
/// <returns>Scheduled Jobs root folder.</returns>
|
|
private ITaskFolder GetRootFolder()
|
|
{
|
|
ITaskFolder iTaskRootFolder = null;
|
|
|
|
try
|
|
{
|
|
iTaskRootFolder = _taskScheduler.GetFolder(ScheduledJobTasksRootFolder);
|
|
}
|
|
catch (System.IO.DirectoryNotFoundException)
|
|
{
|
|
}
|
|
catch (System.IO.FileNotFoundException)
|
|
{
|
|
// This can be thrown if COM interop tries to load the Microsoft.PowerShell.ScheduledJob
|
|
// assembly again.
|
|
}
|
|
|
|
if (iTaskRootFolder == null)
|
|
{
|
|
// Create the PowerShell Scheduled Job root folder.
|
|
ITaskFolder iTSWindowsFolder = _taskScheduler.GetFolder(TaskSchedulerWindowsFolder);
|
|
iTaskRootFolder = iTSWindowsFolder.CreateFolder(ScheduledJobSubFolder);
|
|
}
|
|
|
|
return iTaskRootFolder;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds a task with the provided Task Id and returns it as
|
|
/// a ITaskDefinition object.
|
|
/// </summary>
|
|
/// <param name="taskId">Task Id.</param>
|
|
/// <returns>ITaskDefinition.</returns>
|
|
private ITaskDefinition FindTask(string taskId)
|
|
{
|
|
try
|
|
{
|
|
ITaskFolder iTaskFolder = _taskScheduler.GetFolder(ScheduledJobTasksRootFolder);
|
|
IRegisteredTask iRegisteredTask = iTaskFolder.GetTask(taskId);
|
|
return iRegisteredTask.Definition;
|
|
}
|
|
catch (System.IO.DirectoryNotFoundException e)
|
|
{
|
|
string msg = StringUtil.Format(ScheduledJobErrorStrings.CannotFindTaskId, taskId);
|
|
throw new ScheduledJobException(msg, e);
|
|
}
|
|
}
|
|
|
|
private Int32 ConvertStringId(string triggerId)
|
|
{
|
|
Int32 triggerIdVal = 0;
|
|
|
|
try
|
|
{
|
|
triggerIdVal = Convert.ToInt32(triggerId);
|
|
}
|
|
catch (FormatException)
|
|
{ }
|
|
catch (OverflowException)
|
|
{ }
|
|
|
|
return triggerIdVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to parse a WTS time string and return
|
|
/// a corresponding TimeSpan object. Note that the
|
|
/// year and month values are ignored.
|
|
/// Format:
|
|
/// "PnYnMnDTnHnMnS"
|
|
/// "P" - Date separator
|
|
/// "nY" - year value.
|
|
/// "nM" - month value.
|
|
/// "nD" - day value.
|
|
/// "T" - Time separator
|
|
/// "nH" - hour value.
|
|
/// "nM" - minute value.
|
|
/// "nS" - second value.
|
|
/// </summary>
|
|
/// <param name="wtsTime">Formatted time string.</param>
|
|
/// <returns>TimeSpan.</returns>
|
|
private TimeSpan ParseWTSTime(string wtsTime)
|
|
{
|
|
if (string.IsNullOrEmpty(wtsTime))
|
|
{
|
|
return new TimeSpan(0);
|
|
}
|
|
|
|
int days = 0;
|
|
int hours = 0;
|
|
int minutes = 0;
|
|
int seconds = 0;
|
|
int indx = 0;
|
|
int length = wtsTime.Length;
|
|
StringBuilder str = new StringBuilder();
|
|
|
|
try
|
|
{
|
|
while (indx != length)
|
|
{
|
|
char c = wtsTime[indx++];
|
|
|
|
switch (c)
|
|
{
|
|
case 'P':
|
|
str.Clear();
|
|
while (indx != length &&
|
|
wtsTime[indx] != 'T')
|
|
{
|
|
char c2 = wtsTime[indx++];
|
|
if (c2 == 'Y')
|
|
{
|
|
// Ignore year value.
|
|
str.Clear();
|
|
}
|
|
else if (c2 == 'M')
|
|
{
|
|
// Ignore month value.
|
|
str.Clear();
|
|
}
|
|
else if (c2 == 'D')
|
|
{
|
|
days = Convert.ToInt32(str.ToString(), CultureInfo.InvariantCulture);
|
|
str.Clear();
|
|
}
|
|
else if (c2 >= '0' && c2 <= '9')
|
|
{
|
|
str.Append(c2);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case 'T':
|
|
str.Clear();
|
|
while (indx != length &&
|
|
wtsTime[indx] != 'P')
|
|
{
|
|
char c2 = wtsTime[indx++];
|
|
if (c2 == 'H')
|
|
{
|
|
hours = Convert.ToInt32(str.ToString(), CultureInfo.InvariantCulture);
|
|
str.Clear();
|
|
}
|
|
else if (c2 == 'M')
|
|
{
|
|
minutes = Convert.ToInt32(str.ToString(), CultureInfo.InvariantCulture);
|
|
str.Clear();
|
|
}
|
|
else if (c2 == 'S')
|
|
{
|
|
seconds = Convert.ToInt32(str.ToString(), CultureInfo.InvariantCulture);
|
|
str.Clear();
|
|
}
|
|
else if (c2 >= '0' && c2 <= '9')
|
|
{
|
|
str.Append(c2);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch (FormatException)
|
|
{ }
|
|
catch (OverflowException)
|
|
{ }
|
|
|
|
return new TimeSpan(days, hours, minutes, seconds);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates WTS formatted time string based on TimeSpan parameter.
|
|
/// </summary>
|
|
/// <param name="time">TimeSpan.</param>
|
|
/// <returns>WTS time string.</returns>
|
|
internal static string ConvertTimeSpanToWTSString(TimeSpan time)
|
|
{
|
|
return string.Format(
|
|
CultureInfo.InvariantCulture,
|
|
"P{0}DT{1}H{2}M{3}S",
|
|
time.Days,
|
|
time.Hours,
|
|
time.Minutes,
|
|
time.Seconds);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts DateTime to string for WTS.
|
|
/// </summary>
|
|
/// <param name="dt">DateTime.</param>
|
|
/// <returns>DateTime string.</returns>
|
|
internal static string ConvertDateTimeToString(DateTime? dt)
|
|
{
|
|
if (dt == null)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
else
|
|
{
|
|
return dt.Value.ToString("s", CultureInfo.InvariantCulture);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a bitmask representing days of week as
|
|
/// required by Windows Task Scheduler API.
|
|
/// </summary>
|
|
/// <param name="daysOfWeek">Array of DayOfWeek.</param>
|
|
/// <returns>WTS days of week mask.</returns>
|
|
internal static short ConvertDaysOfWeekToMask(IEnumerable<DayOfWeek> daysOfWeek)
|
|
{
|
|
short rtnValue = 0;
|
|
foreach (DayOfWeek day in daysOfWeek)
|
|
{
|
|
switch (day)
|
|
{
|
|
case DayOfWeek.Sunday:
|
|
rtnValue |= WTSSunday;
|
|
break;
|
|
|
|
case DayOfWeek.Monday:
|
|
rtnValue |= WTSMonday;
|
|
break;
|
|
|
|
case DayOfWeek.Tuesday:
|
|
rtnValue |= WTSTuesday;
|
|
break;
|
|
|
|
case DayOfWeek.Wednesday:
|
|
rtnValue |= WTSWednesday;
|
|
break;
|
|
|
|
case DayOfWeek.Thursday:
|
|
rtnValue |= WTSThursday;
|
|
break;
|
|
|
|
case DayOfWeek.Friday:
|
|
rtnValue |= WTSFriday;
|
|
break;
|
|
|
|
case DayOfWeek.Saturday:
|
|
rtnValue |= WTSSaturday;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rtnValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts WTS days of week mask to an array of DayOfWeek type.
|
|
/// </summary>
|
|
/// <param name="mask">WTS days of week mask.</param>
|
|
/// <returns>Days of week as List.</returns>
|
|
private List<DayOfWeek> ConvertMaskToDaysOfWeekArray(short mask)
|
|
{
|
|
List<DayOfWeek> daysOfWeek = new List<DayOfWeek>();
|
|
|
|
if ((mask & WTSSunday) != 0) { daysOfWeek.Add(DayOfWeek.Sunday); }
|
|
|
|
if ((mask & WTSMonday) != 0) { daysOfWeek.Add(DayOfWeek.Monday); }
|
|
|
|
if ((mask & WTSTuesday) != 0) { daysOfWeek.Add(DayOfWeek.Tuesday); }
|
|
|
|
if ((mask & WTSWednesday) != 0) { daysOfWeek.Add(DayOfWeek.Wednesday); }
|
|
|
|
if ((mask & WTSThursday) != 0) { daysOfWeek.Add(DayOfWeek.Thursday); }
|
|
|
|
if ((mask & WTSFriday) != 0) { daysOfWeek.Add(DayOfWeek.Friday); }
|
|
|
|
if ((mask & WTSSaturday) != 0) { daysOfWeek.Add(DayOfWeek.Saturday); }
|
|
|
|
return daysOfWeek;
|
|
}
|
|
|
|
private TaskMultipleInstancePolicy ConvertToMultiInstances(
|
|
ITaskSettings iTaskSettings)
|
|
{
|
|
switch (iTaskSettings.MultipleInstances)
|
|
{
|
|
case _TASK_INSTANCES_POLICY.TASK_INSTANCES_IGNORE_NEW:
|
|
return TaskMultipleInstancePolicy.IgnoreNew;
|
|
|
|
case _TASK_INSTANCES_POLICY.TASK_INSTANCES_PARALLEL:
|
|
return TaskMultipleInstancePolicy.Parallel;
|
|
|
|
case _TASK_INSTANCES_POLICY.TASK_INSTANCES_QUEUE:
|
|
return TaskMultipleInstancePolicy.Queue;
|
|
|
|
case _TASK_INSTANCES_POLICY.TASK_INSTANCES_STOP_EXISTING:
|
|
return TaskMultipleInstancePolicy.StopExisting;
|
|
}
|
|
|
|
Debug.Assert(false);
|
|
return TaskMultipleInstancePolicy.None;
|
|
}
|
|
|
|
private _TASK_INSTANCES_POLICY ConvertFromMultiInstances(
|
|
TaskMultipleInstancePolicy jobPolicies)
|
|
{
|
|
switch (jobPolicies)
|
|
{
|
|
case TaskMultipleInstancePolicy.IgnoreNew:
|
|
return _TASK_INSTANCES_POLICY.TASK_INSTANCES_IGNORE_NEW;
|
|
|
|
case TaskMultipleInstancePolicy.Parallel:
|
|
return _TASK_INSTANCES_POLICY.TASK_INSTANCES_PARALLEL;
|
|
|
|
case TaskMultipleInstancePolicy.Queue:
|
|
return _TASK_INSTANCES_POLICY.TASK_INSTANCES_QUEUE;
|
|
|
|
case TaskMultipleInstancePolicy.StopExisting:
|
|
return _TASK_INSTANCES_POLICY.TASK_INSTANCES_STOP_EXISTING;
|
|
|
|
default:
|
|
return _TASK_INSTANCES_POLICY.TASK_INSTANCES_IGNORE_NEW;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDisposable
|
|
|
|
/// <summary>
|
|
/// Dispose.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
// Release reference to Task Scheduler object so that the COM
|
|
// object can be released.
|
|
_iRootFolder = null;
|
|
_taskScheduler = null;
|
|
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|