Scripting in Windows Vista

Task Scheduler: Part 1

Windows Vista

The following article is based on a pre-release version of the Microsoft Windows Vista operating system. That means that the features discussed in the article are subject to change: some may not be included in the final product due to marketing, technical, or other reasons. We will do our best to update this article if and when such changes occur.

*
**
**
On This Page
Scripting Task SchedulerScripting Task Scheduler
Listing TasksListing Tasks
Setting a Daily TaskSetting a Daily Task
Just the BeginningJust the Beginning

Scripting Task Scheduler

Your Outlook reminder just went off – time to go to that planning meeting. Here comes another reminder – dentist appointment. Now your PDA is beeping at you – time to pick up the kids from school. You get home, your significant other is beeping at you – uh, reminding you – that you should be mowing the lawn. Your life is a constant barrage of reminders to do something. Wouldn’t it be nice if some of these things could just take care of themselves without you having to do anything?

Thank goodness for Task Scheduler. You set the reminder, and then – here’s the great part – Task Scheduler will perform the action for you. What could be better than that?

Actually, we can think of one thing: It would be great if you could script Task Scheduler. You always had two choices with Task Scheduler: use the GUI or learn C++. For most system administrators that was a pretty easy choice.

Note: Technically there was (and still is) one more choice for scripting scheduled tasks: a command-line tool named At.exe. (In Windows Vista a command-line tool is still available, named Schtasks.exe.) However, working with At.exe is different from scripting the Task Scheduler API. At.exe included an API that was scriptable through the Win32_ScheduledJobs class, but it lacked quite a bit of the functionality and flexibility available in the Task Scheduler tool and the Task Scheduler API. You can find out about scripting with At.exe in the Windows 2000 Scripting Guide.

In Windows Vista, however, you can access the Task Scheduler API through scripts. You’ll be amazed at the scripting capabilities of this tool (we certainly are). There are so many we could write a book, or at least a long chapter, all about scripting Task Scheduler 2.0, the latest version of Task Scheduler available only on Windows Vista. Since we don’t have time for that right now (the PDA is beeping again), we’re going to give you a look at just a few things you can do, and maybe we’ll get to more of them in Part 2. If there is a Part 2.

Top of pageTop of page

Listing Tasks

Before we get started with the scripting, let’s take a quick look at the new Task Scheduler:

Task Scheduler


Click here to see a full-size version of this screen shot.

Obviously we’re not going to explain how this all works; that’s what Help is for. We just wanted to point out a couple things. First, notice the pane on the left side. This is where your folders are. Just like with any other portion of file system (after all, scheduled tasks are just files stored in a special folder), you can organize your tasks into folders to make them easier to keep track of. When you select a folder, the tasks in that folder will be displayed in the center pane, along with information about those tasks. The right pane includes actions you can take on those tasks and folders.

The first script we’re going to look at is a simple script that looks in the root folder of your Task Scheduler and lists all the tasks in that folder. Sounds pretty simple, doesn’t it? And, as a matter of fact, it is:

Set service = CreateObject("Schedule.Service")
service.Connect

Set rootFolder = service.GetFolder("\")
 
Set colTasks = rootFolder.GetTasks(0)

For Each task In colTasks
    Wscript.Echo task.Name
Next

The first thing we do in this script is connect to the Task Scheduler service. We do that with these two lines of code:

Set service = CreateObject("Schedule.Service")
service.Connect

Line one uses the CreateObject method to create an instance of Schedule.Service, the Task Scheduler service. The second line uses the Connect method to actually connect to the service. You might be wondering why you would need to connect to the service; isn’t creating the service enough? No, it’s not. Why not? Because we need to specify which computer we’re connecting to. By default we connect to the local computer, which is what we’re doing here. However, we could have specified a remote computer to connect to, like this:

service.Connect("atl-fs-01")

The Connect method also allows you to specify a user name, domain, and password to use to connect to the remote computer.

Now that we’re connected to the service we need to retrieve an object reference to the folder we want to search. In this case, that’s the root folder (\):

Set rootFolder = service.GetFolder("\")

We then use the GetTasks method of the folder object to get a collection of all the tasks in the folder:

Set colTasks = rootFolder.GetTasks(0)

Incidentally, the parameter to the GetTasks method is required, and it has to be a zero. Don’t ask why, it’s just one of those things.

The last thing we do is use a For Each loop to loop through the collection of tasks and echo the name of each task to the screen. Like we said, pretty simple.

What’s that? What if you want to look at the tasks in all the folders? No problem:

Set service = CreateObject("Schedule.Service")
service.Connect

Set rootFolder = service.GetFolder("\")

GetAllTasks rootFolder

Sub GetAllTasks(folder)

    Set colTasks = folder.GetTasks(0)

    For Each task in colTasks
        Wscript.Echo task.Path
    Next

    Set colFolders = folder.GetFolders(0)
    For Each f in colFolders
        GetAllTasks f
    Next
End Sub

This one is a little more complicated because we need to use recursion. If you don’t know what recursion is, take a look at the Recursion section in the Windows 2000 Scripting Guide. But, other than the recursive part, this script is pretty much the same as the last one. We start out by connecting to the Task Scheduler service, then use the GetFolder method to get an object reference to the root folder.

Next we call the GetAllTasks subroutine, passing it the object reference to the root folder. Inside the subroutine we call GetTasks on the folder, just like we did before, to get a collection of all the tasks in the folder. We then loop through the collection and this time echo the Path of the task (rather than just the name) so we can see what folder the task is in.

After that we call the GetFolders method on the folder object. Notice the “s” on the end: this method returns a collection of all the subfolders within the folder. We then enter a For Each loop where we again call the GetAllTasks subroutine, passing it a reference to each folder object in turn. Yes, we are calling the GetAllTasks subroutine from within the GetAllTasks subroutine. It seems kind of weird, but that’s what recursion is all about. Like we said, read the Recursion section to get a good handle on this; in the meantime trust us when we say that this works. With each subfolder we call GetAllTasks, which will then get all the tasks for that folder, echo back the Name and Path of each task, then retrieve all of its subfolders and do the same thing with them.

The results look something like this:

\Test task
\Microsoft\Test task 2
\MyTasks\Test Daily Trigger
Top of pageTop of page

Setting a Daily Task

That wasn’t too painful, was it? All right, the recursion was a little painful, but not too bad. Now we’re ready for some real fun – let’s schedule a task. (Ignore that ringing telephone; they’ll call back.) For our first task, we’re going to schedule a backup of our system to run every day. Here’s the script:

Const TASK_TRIGGER_DAILY = 2
Const TASK_ACTION_EXEC = 0 
Const TASK_CREATE = 2

Set objService = CreateObject("Schedule.Service")
objService.Connect

Set objFolder = objService.GetFolder("\MyTasks")

Set objTaskDefinition = objService.NewTask(0)

Set colTasks = objTaskDefinition.Triggers

Set objTrigger = colTasks.Create(TASK_TRIGGER_DAILY)

objTrigger.DaysInterval = 1
objTrigger.StartBoundary = "2006-06-27T08:00:00-00:00"


Set colActions = objTaskDefinition.Actions

Set objAction = colActions.Create(TASK_ACTION_EXEC)
objAction.ID = "Daily Task Test"
objAction.Path = "C:\Windows\System32\sdclt.exe"

Set objInfo = objTaskDefinition.RegistrationInfo

objInfo.Author = "Administrator"
objInfo.Description = "Test task that displays Windows Backup Status and Configuration tool daily."

Set objSettings = objTaskDefinition.Settings
objSettings.Enabled = True
objSettings.Hidden = False

objFolder.RegisterTaskDefinition "Test Daily Trigger", objTaskDefinition, TASK_CREATE,  , , 0

Yes, we know, this script looks kind of long and kind of complicated. But don’t worry, it’s really not much different from what you have to do in the GUI to create a task. Let’s walk through the steps we need to go through to create a daily task.

Connect to Task Scheduler

We already saw how to do this in earlier examples:

Set objService = CreateObject("Schedule.Service")
objService.Connect

We simply create a Schedule.Service object and call the Connect method on that object to connect to the Task Scheduler service.

Note: We already skipped something, didn’t we? The first thing we do is declare some constants: TASK_TRIGGER_DAILY, TASK_ACTION_EXEC, and TASK_CREATE. We’ll use these later to define the type of task we’re going create, the type of action that will occur when the task runs, and the way in which we want to register the task.

Create a New Task in a Specified Folder

The next two lines of code first connect us to the folder in which we want to create the task and then create the task:

Set objFolder = objService.GetFolder("\MyTasks")

Set objTaskDefinition = objService.NewTask(0)

As you can see, we want our task to be created in the MyTasks folder; therefore we retrieve an object reference (objFolder) to that folder. We then call the NewTask method of the Task Scheduler service to create a reference to the TaskDefinition object. We’ll use this object reference to define the type of task and the actions associated with it. Notice in the call to NewTask we passed a single parameter of 0. This parameter is required, and it must be 0.

Create the Task Trigger

We want this task to occur at a specific time every day, which means we need a trigger that will activate our task at that time. To specify that we need to create a daily trigger:

Set colTasks = objTaskDefinition.Triggers
Set objTrigger = colTasks.Create(TASK_TRIGGER_DAILY)
objTrigger.DaysInterval = 1
objTrigger.StartBoundary = "2006-06-27T08:00:00-00:00"

Here we use our TaskDefinition object to retrieve a collection of all the triggers associated with our task. Because we haven’t actually defined any triggers yet this gives us an empty collection object (a TriggerCollection object). You might be wondering why in the world we’d retrieve an empty collection. Well, we do that because we can use the Create method of the collection object to create a new trigger. We pass the Create method the constant we defined earlier, TASK_TRIGGER_DAILY; this constant specifies that we’re creating a DailyTrigger. You can create many different types of triggers, and the object returned by the Create method depends on the value you pass as a parameter. Here are the possible values you can use to create a trigger:

TriggerValueDescription

TASK_TRIGGER_EVENT

0

Task is triggered when a specified event occurs

TASK_TRIGGER_TIME

1

Task is triggered at a specific time of day

TASK_TRIGGER_DAILY

2

Task is triggered based on a specified number of days (every day, every two days, etc.)

TASK_TRIGGER_WEEKLY

3

Task is triggered on a weekly basis, on a particular day and time each week (or every other week)

TASK_TRIGGER_MONTHLY

4

Task is triggered monthly, such as on a specific day each month

TASK_TRIGGER_MONTHLYDOW

5

Task is triggered on a monthly day-of-week schedule (specific days of the week, weeks of the month, months of the year, etc.)

TASK_TRIGGER_IDLE

6

Task is triggered when the computer enters an idle state

TASK_TRIGGER_REGISTRATION

7

Task is triggered when it’s registered (created)

TASK_TRIGGER_BOOT

8

Task is triggered when the computer is booted up

TASK_TRIGGER_LOGON

9

Task is triggered when a specified user logs on

TASK_TRIGGER_SESSION_STATE_CHANGE

11

Task is triggered when a specified session state change occurs, such as when a user locks or unlocks the computer

Now that we’ve created the trigger we need to define what we mean by “daily.” A daily task can occur every day, every two days, every third day, and so on. To specify how often we want our task to occur, we set the DaysInterval property:

objTrigger.DaysInterval = 1

We set this property to 1 to make the task will occur every day. If we’d wanted the task to occur every other day, we’d set the value to 2. If we wanted it to occur every three days, … well, you get the idea.

Next we need to set the start date for our scheduled task. We do that by setting the StartBoundary property of our DailyTrigger object:

' YYYY-MM-DDTHH:MM:SS(+-)HH:MM
objTrigger.StartBoundary = "2006-07-21T08:00:00-00:00"

You can set a date and a time for the task to start. The date is in the format YYYY-MM-DDTHH:MM:SS(+-)HH:MM. This task is set to start on July 21, 2006 at 8:00 AM GMT (Greenwich Mean Time). Why Greenwich Mean Time? Because we didn’t specify an offset. The last six characters of the string, in this case “-00:00” specifies plus or minus the time differential to GMT. For example, if we wanted this to be 8:00 AM Pacific Daylight Time, we’d use an offset of “-07:00”, which would make our StartBoundary value look like this:

objTrigger.StartBoundary = "2006-07-21T08:00:00-07:00"

Note: One other option you can set when defining a daily trigger is the random delay. By setting the RandomDelay property, the task will randomly be delayed up to the amount of time specified in the property. The value is in the format P<days>DT<hours>H<minutes>M<seconds>S. For example, this will randomly delay the start of the task up to 3 days, 5 hours, and 27 minutes:

objTrigger.RandomDelay = "P3DT5H27MS"

Another property that’s available on a trigger is the Enable property. By default this property is True, which means – yes, that’s right – the trigger is enabled. If you set this property to False you’ll disable the trigger and the task won’t run:

objTrigger.Enabled = False

There are several other trigger properties that you can get or set for a daily trigger; for a full list see the Task Scheduler Technical Reference on MSDN.

Define the Task Action

Once we know what is going to trigger our task we need to specify the action that task is going to perform.

Set colActions = objTaskDefinition.Actions

Set objAction = colActions.Create(TASK_ACTION_EXEC)
objAction.ID = "Daily Task Test"
objAction.Path = "C:\Windows\System32\sdclt.exe"

Once again we need to use our TaskDefinition object, this time to retrieve a collection of actions (the ActionCollection object). Remember, earlier in the script we used the TaskDefinition object to retrieve a collection of triggers. Just as it did with the triggers the TaskDefinition object returns an empty collection; that’s because this is a new task on which we haven’t defined any actions defined.

The next thing we do is call the Create method on the ActionCollection object (colActions) to create an action object. Again, just as there are different types of triggers there are different types of actions. In this script we want the task to run an executable file, so we need to pass the TASK_ACTION_EXEC constant we defined at the beginning of the script to the Create method. This will create an action object of type ExecAction. Here’s a list of the types of actions you can create:

ActionValueDescription

TASK_ACTION_EXEC

0

Execute a command-line operation, such as starting an executable file or script

TASK_ACTION_COM_HANDLER

5

Fire a handler

TASK_ACTION_SEND_EMAIL

6

Send an e-mail message

TASK_ACTION_SHOW_MESSAGE

7

Show a message in a message box

Now that we have an action object we can define some properties of that object. First we define an ID to be able to identify the action:

objAction.ID = "Daily Task Test"

Here’s the really critical part: if you don’t do this your task won’t know what to do. We set the Path property of our action object:

objAction.Path = "C:\Windows\System32\sdclt.exe"

As you can see, the Path property contains the full path to the executable file we want to run when the task occurs. In this case we’re running the new Windows Backup Status and Configuration tool found in Windows Vista.

There are two other properties of the ExecAction object which relate to the Path property: WorkingDirectory and Arguments. You can use WorkingDirectory to set the folder you’ll be using. This comes in handy if you’re going to be running multiple files or if your file depends on other files in the same folder. If we were to set the WorkingDirectory property we wouldn’t need to specify the full path in the Path property:

objAction.WorkingDirectory = "C:\Windows\System32\"
objection.Path = "sdclt.exe"

You can use the Arguments property to specify arguments for the executable or script you’re running. You can also specify these arguments as part of the Path, so that you don’t have to use the Arguments property:

objAction.Arguments = "arg1 arg2"

Configure Task Information and Settings

We’ve now created a task in a folder within Task Scheduler and we’ve defined the trigger that will cause the task to run and the action that will occur at that time. That’s all we need to do, right? Well, not quite. But don’t worry, we’re almost to the end. This really isn’t that complicated, it’s just kind of long. As you probably figured out for yourself.

Now we’re going to configure some information that will allow Task Scheduler to identify and keep track of this task. We don’t have to do this, but it provides extra information about the task that might help us remember what it’s all about later. (Yes, another reminder.)

Set objInfo = objTaskDefinition.RegistrationInfo

objInfo.Author = "Administrator"
objInfo.Description = "Test task that displays Windows Backup Status and Configuration tool daily."

Set objSettings = objTaskDefinition.Settings
objSettings.Enabled = True
objSettings.Hidden = False

Once again we’re relying on our old friend the TaskDefinition object. This time we’re using the RegistrationInfo property to retrieve the task’s RegistrationInfo object. The RegistrationInfo object contains properties that define the task. We set the Author property to the creator and owner of the task, and we also set a Description property so when we see the task in Task Scheduler we’ll be reminded what it’s for.

We then use the TaskDefinition object – yes, again – to retrieve the TaskSettings object. The TaskSettings object provides around 20 properties that you can use to define a task. We’re not going to go over all of them here; for that see the Task Scheduler Technical Reference. For this simple example we set only two properties:

objSettings.Enabled = True
objSettings.Hidden = False

First we set the Enabled property to True. If this property is False, the task won’t run; it’s kind of like turning the task off. We also set the Hidden property to False. If this property is set to True, the task will be hidden from the user interface. (Incidentally, both of these values are the defaults for these properties; if you’re fine with the defaults you can omit these lines. We showed them here just for demonstration purposes.)

Register the Task

The very last thing we do in this script is register the task:

objFolder.RegisterTaskDefinition "Test Daily Trigger", objTaskDefinition, TASK_CREATE,  , , 0

Remember, towards the very beginning of the script, when we retrieved an object reference to the folder we want to save the task to? Well, we’re finally going to use it. Now that we’ve used the TaskDefinition object – over and over again – to define the task, we need to actually register that task in Task Scheduler. (“Registering” the task basically means creating it in Task Scheduler.) We do that by calling the RegisterTaskDefinition method on the folder object.

As you can see, we passed a few parameters to RegisterTaskDefinition. The first parameter is required and contains the name we want to give to the task; this is the name you’ll see listed in Task Scheduler. You’ll need to keep track of your task names, because the name you give it must be unique within a given task folder. The second parameter, objTaskDefinition, is of course our trusty TaskDefinition object. The third parameter, TASK_CREATE, is a constant with a value of 2 that says we want to create the task in Task Scheduler (as opposed to update, disable, or perform some other action on the task).

After that we have a bunch of commas. What we’re doing here is skipping parameters. We have no need to define the fourth or fifth parameters, so we leave the commas in as placeholders, enabling us to get to the sixth parameter. This parameter defines what type of logon is required to run the task. A value of 0 means the task will run only when the user is logged on and that you don’t need to specify any credentials to register the task. (In case you were wondering, the fourth and fifth parameters we left out allow you to provide a user name and password to register the task.)

After we run this script, we can look in Task Scheduler and see the results:

Task Scheduler


Click here to see a full-size version of this screen shot.

Top of pageTop of page

Just the Beginning

We’ve barely scratched the surface of what you can script in Task Scheduler in Windows Vista. We can’t promise when the next installment will be, but since we don’t think it’s fair to whet your appetite and walk away we’ll point you to the Task Scheduler documentation available on MSDN. Okay, we do think it’s fair to whet your appetite and walk away, but since the documentation is there we might as well help you find it.

Oops, another reminder just went off, we’re going to be late for our next meeting. Gotta go.


Top of pageTop of page