Create A Scheduled Task Programmatically

stop-this-watch-487816-m

Previously, we discussed how you can leverage the Task Scheduler Managed Wrapper to automate backing up your scheduled task definitions to an XML format. This only scratches the surface of what you can do with the task scheduler API, however. Today we will use the API to create a new scheduled task programmatically. Since we love Windows Services, our case study will be creating a restart job for a service.

To begin, let’s set out a basic API. We want to create a service restart task, so the following API seems like a good place to start:

using System;
using Microsoft.Win32.TaskScheduler;

namespace DOW
{
    public static class ServiceTask
    {
        public static void CreateRestartTask(string serviceName)
        {
        }
    }
}

To fill out this method, we start by creating an instance of the TaskService class, which is the main mechanism for accessing the task scheduler. The TaskService acts as a factory for tasks via the NewTask method, which returns an instance of TaskDefinition:

using(var ts = new TaskService())
{
    var task = ts.NewTask();
}

To create the task, it must be registered in a particular folder:

ts.RootFolder.RegisterTaskDefinition(serviceName + " Restart", task);

Unfortunately, this code fails with a rather non-obvious COM Exception:

TaskSchedulerComException

You’d be hard pressed to figure it out from the exception, but the task needs be configured to do something useful before it can be registered successfully (it must have an associated action). To configure it to restart the named service, we add an action via the TaskDefinition.Actions property:

task.Actions.Add(new ExecAction("cmd.exe",
    string.Format("/c net stop {0} & net start {0}", serviceName));

Here we are using the net command to restart the service. The code now executes successfully, and we see the task appear in the task scheduler’s root folder. However, if you try to run the task manually, it will fail with an “access is denied” exception (assuming you have User Account Control enabled). To address this, either disable UAC (always a valid option in my book), or you can configure the task to effectively “run as administrator”:

    task.Principal.RunLevel = TaskRunLevel.Highest;

Note that you will need to run the application that creates the task as an administrator as well if you use this approach. Now that we know the task can run successfully, let’s add a trigger so it will run automatically. To do so, use the TaskDefinition.Triggers collection:

task.Triggers.Add(new DailyTrigger()
    { StartBoundary = DateTime.Today.AddHours(19) });

Here we have added a daily trigger that fires every day at 7 PM. Many other triggers are available, including BootTrigger and WeeklyTrigger. Putting it all together, our code for creating a scheduled restart task looks like this:

using System;
using Microsoft.Win32.TaskScheduler;

namespace DOW
{
    public static class ServiceTask
    {
        public static void CreateRestartTask(string serviceName)
        {
            using (var ts = new TaskService())
            {
                var task = ts.NewTask();
                task.Actions.Add(new ExecAction("cmd.exe",
                    string.Format("/c net stop {0} & net start {0}", serviceName)));
                task.Triggers.Add(
                    new DailyTrigger() { StartBoundary = DateTime.Today.AddHours(19) });
                task.Principal.RunLevel = TaskRunLevel.Highest;
                ts.RootFolder.RegisterTaskDefinition(serviceName + " Restart", task);
            }
        }
    }
}

Creating scheduled tasks programmatically is a good idea because it helps ensure consistency across your tasks. Heck, we could even add the creation of this restart task to the Windows Service framework and have our applications manage their own restarts!

Leave a Reply

Your email address will not be published. Required fields are marked *