If you are creating windows forms application that has to perform some tasks on the regular basis the good solution is to use built-in Windows Task Scheduler. It is very stable and reliable as long as the user’s computer is working.
In this article I will show you how to create scheduled task by using Microsoft.Win32.TaskScheduler.dll (managed wrapper dll).
Let’s create our Scheduler class with the function to set up win scheduler. In our example we will run either an external file or test application depending on the parameters passed in.
After creating new instance of the TaskService, we need to set up some basic parameters (names and description to be shown in windows scheduler UI). Next, we need to create trigger, in our case we will use weekly trigger as the task will be performed every day of the week that user will configure.
After that we have to set up execution path and additional startup arguments if required. This is useful to detect from the target application that it has been run automatically and based on that to apply different layout, colors, buttons etc.
public class Scheduler
{
public static string TaskName = "MyTask";
public static void SetTask(string filePath, DateTime startDate, DaysOfTheWeek daysOfWeek, bool enabled)
{
var ts = new TaskService();
var td = ts.NewTask();
td.RegistrationInfo.Author = "My company";
td.RegistrationInfo.Description = "Runs test application";
td.Triggers.Add(new WeeklyTrigger { StartBoundary = startDate, DaysOfWeek = daysOfWeek, Enabled = enabled });
//run this application or setup path to the file
var action = new ExecAction(Assembly.GetExecutingAssembly().Location, null, null);
if (filePath != string.Empty && File.Exists(filePath))
{
action = new ExecAction(filePath);
}
action.Arguments = "/Scheduler";
td.Actions.Add(action);
ts.RootFolder.RegisterTaskDefinition(TaskName, td);
}
Next let’s create other basic functions to get and delete current task. This is quite straightforward.
public static void DeleteTask()
{
var ts = new TaskService();
var task = ts.RootFolder.GetTasks().Where(a => a.Name.ToLower() == TaskName.ToLower()).FirstOrDefault();
if (task != null)
{
ts.RootFolder.DeleteTask(TaskName);
}
}
public static Task GetTask()
{
var ts = new TaskService();
var task = ts.RootFolder.GetTasks().Where(a => a.Name.ToLower() == TaskName.ToLower()).FirstOrDefault();
return task;
}
Now, lets create function to display next scheduled task run date. It is quite useful to notify the user about the next planned execution. In our implementation we will use NextRunTime property of the task we created. We also have to check if the task is enabled.
public static string GetNextScheduleTaskDate()
{
try
{
var task = Scheduler.GetTask();
if (task != null)
{
var trigger = task.Definition.Triggers.FirstOrDefault();
if (trigger != null)
{
if (trigger.TriggerType == TaskTriggerType.Weekly)
{
if (trigger.Enabled)
{
var weeklyTrigger = (WeeklyTrigger)trigger;
return task.NextRunTime.ToString("yyyy MMM. dd dddd 'at ' HH:mm");
}
}
}
}
}
catch (Exception)
{ }
return "no scheduled date!";
}
Within the UI, we have to create basic helper methods in order to support interface values. In our case we will use checkboxes to set and enable task. In our example, the user will be able to set the task in any day of the week, disable it or delete it permanently. We will also display the next run time information.
void RefreshNextRunInfo()
{
lblNextRun.Text = Scheduler.GetNextScheduleTaskDate();
}
//Load selected days from checkboxes
DaysOfTheWeek GetTaskDays()
{
var daysOfWeek = DaysOfTheWeek.AllDays;
daysOfWeek &= ~DaysOfTheWeek.AllDays;
if (chkbMonday.Checked) { daysOfWeek = daysOfWeek | DaysOfTheWeek.Monday; }
if (chkbSaturday.Checked) { daysOfWeek = daysOfWeek | DaysOfTheWeek.Saturday; }
if (chkbSunday.Checked) { daysOfWeek = daysOfWeek | DaysOfTheWeek.Sunday; }
if (chkbThursday.Checked) { daysOfWeek = daysOfWeek | DaysOfTheWeek.Thursday; }
if (chkbTuestday.Checked) { daysOfWeek = daysOfWeek | DaysOfTheWeek.Tuesday; }
if (chkbWednesday.Checked) { daysOfWeek = daysOfWeek | DaysOfTheWeek.Wednesday; }
if (chkbFriday.Checked) { daysOfWeek = daysOfWeek | DaysOfTheWeek.Friday; }
return daysOfWeek;
}
void SetTaskForm(bool enabled)
{
timePicker.Enabled = enabled;
chkbFriday.Enabled = enabled;
chkbMonday.Enabled = enabled;
chkbSaturday.Enabled = enabled;
chkbSunday.Enabled = enabled;
chkbThursday.Enabled = enabled;
chkbTuestday.Enabled = enabled;
chkbWednesday.Enabled = enabled;
}
//reload current task state (if exists)
public void RefreshSchedulerSettings()
{
//set initial time to 5 minutes ahead for testing
timePicker.Value = DateTime.Now.AddMinutes(5);
try
{
SetTaskForm(false);
chkbFriday.Checked = false;
chkbMonday.Checked = false;
chkbSaturday.Checked = false;
chkbSunday.Checked = false;
chkbThursday.Checked = false;
chkbTuestday.Checked = false;
chkbWednesday.Checked = false;
chkbScheduler.Checked = false;
var task = Scheduler.GetTask();
if (task != null)
{
var trigger = task.Definition.Triggers.FirstOrDefault();
if (trigger != null)
{
if (trigger.TriggerType == TaskTriggerType.Weekly)
{
if (trigger.Enabled) { SetTaskForm(true); }
chkbScheduler.Checked = trigger.Enabled;
var weeklyTrigger = (WeeklyTrigger)trigger;
timePicker.Value = trigger.StartBoundary;
if (weeklyTrigger.DaysOfWeek.HasFlag(DaysOfTheWeek.Friday)) { chkbFriday.Checked = true; }
if (weeklyTrigger.DaysOfWeek.HasFlag(DaysOfTheWeek.Monday)) { chkbMonday.Checked = true; }
if (weeklyTrigger.DaysOfWeek.HasFlag(DaysOfTheWeek.Saturday)) { chkbSaturday.Checked = true; }
if (weeklyTrigger.DaysOfWeek.HasFlag(DaysOfTheWeek.Sunday)) { chkbSunday.Checked = true; }
if (weeklyTrigger.DaysOfWeek.HasFlag(DaysOfTheWeek.Thursday)) { chkbThursday.Checked = true; }
if (weeklyTrigger.DaysOfWeek.HasFlag(DaysOfTheWeek.Tuesday)) { chkbTuestday.Checked = true; }
if (weeklyTrigger.DaysOfWeek.HasFlag(DaysOfTheWeek.Wednesday)) { chkbWednesday.Checked = true; }
}
}
}
}
catch (Exception)
{ }
}
When saving the task we will use following method that validates and gathers user input information. Your implementation of course will depend on your requirements.
private void btnSave_Click(object sender, EventArgs e)
{
var filePath = string.Empty;//set path to the exe file or leave it empty to run this app for test
var daysOfWeek = GetTaskDays();
if (chkbScheduler.Checked && daysOfWeek == 0)
{
MessageBox.Show(this, "Select at least one day or turn off scheduler!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
try
{
if (daysOfWeek != 0)
{
var startDate = DateTime.Now.Date.AddHours(timePicker.Value.Hour).AddMinutes(timePicker.Value.Minute);
Scheduler.SetTask(filePath, timePicker.Value, daysOfWeek, chkbScheduler.Checked);
}
else
{
Scheduler.DeleteTask();
}
}
catch (Exception ex)
{
MessageBox.Show(this, "An error occured " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
RefreshNextRunInfo();
}
I have included fully working example below for your tests.
TaskScheduler