This article describes a way to create the Scheduled tasks in a system pro-grammatically for Windows Vista, Windows 7 etc Operating Systems using C#.NET language. Windows operating systems, though, already provide a User Interface to see a complete list, to add, to update or to delete a Scheduled Task under it. This UI can be seen by launching a Computer Management by issuing a command compmgmt.msc on a command prompt.
System Requirements
Windows Vista or Windows 7 or higher versions Operating systems
Development Environment
- Development IDE – Visual Studio [or simple Notepad]
- Task Scheduler COM Library
How to Implement?
To begin with, we need an underlying library, can be COM library or unmanaged C(++) library or managed wrapper which talks to the system to get things done.
Aaannnnnnnn.
Don’t worry.
Here is the trick.
If you dare to 🙂 browse to C:\Windows\syswow64, you will notice the presence of Taskschd.dll assembly. This is what we need to proceed with.
Steps:
- Create a new Project using Visual Studio
- Right Click on project and click Add Reference.Select “TaskScheduler TypeLibrary”
The last step resulted in an Interop assembly generation and referenced in the project. You can check this by selecting a referenced assembly and viewing it’s properties.
Now, we’ll make use of Types defined/exported in the interop assembly to achieve:
– View the list of all Scheduled Tasks
– To pro-grammatically add a Scheduled Task
– To delete a task
Be it any CRUD operation, we’ll make use of ITaskService type which connects to an actual store and exposes the operations. We’ll instantiate the TaskService and call the Connect method before any valid operation.
Before detailing out the steps, another important aspect is to understand the layout or a structure, the tasks are organized.
Scheduled Tasks are organized in a hierarchical fashion. They are always stored in a folder. The very first folder is termed Root Folder with a path @”\” or “\\”. Each folder can have sub folders under it. View the above picture to understand or open “Scheduled Tasks” on your system.
The following sections detail out “How to ?” part for each operation.
How to retrieve the list of all Scheduled Tasks?
- Instantiate TaskService object.
ITaskService taskService = new TaskScheduler();
- Call Connect() on previously created taskService object.
taskService.Connect();
- Get a reference to Root Folder.
ITaskFolder rootFolder = taskService.GetFolder(@"\");
Remember, path to Root Folder is “\”. - Make a call to GetTasks() on a folder.
IRegisteredTaskCollection tasks = rootFolder.GetTasks(0); // 0 or 1: 1 will include all hidden tasks as well
Note: Here, you will get those tasks only which are created in root folder. Since this root folder can have sub folders under it, you have to recursively call GetTasks() for each sub-folder.private void Load() { ITaskService taskService = new TaskScheduler();taskService.Connect();List tasks = new List();// “\\” or @”\” is the RootFolder this.GetData(taskService.GetFolder(@”\”), tasks); this.view.ScheduledTasks = tasks; } private void GetData(ITaskFolder folder, List tasks) { foreach (IRegisteredTask task in folder.GetTasks(1)) // get all tasks including those which are hidden, otherwise 0 { tasks.Add(task); System.Runtime.InteropServices.Marshal.ReleaseComObject(task); // release COM object foreach (ITaskFolder subFolder in folder.GetFolders(1)) { this.GetData(subFolder, tasks); } } System.Runtime.InteropServices.Marshal.ReleaseComObject(folder); }
How to delete a Scheduled Task?
We need some task details like task name, task container i.e. folder in which this task exists in order to delete it.
By default, each task’s Location property contains the Name of that particular task as well as it’s containing folder. So if we have an access to Task Location only, we can find out it’s name and it’s containing folder through some string manipulation.
For e.g., “\\SampleTaskFolder\\SampleTask” means that the task named SampleTask is stored in a folder SampleTaskFolder which is further in RootFolder(“\\”).
Steps to delete a task when it’s full location is provided as input:
- Find out the name of Task and it’s containing folder through string manipulation. [You may want to Validate the input:task Location]
- Instantiate a task service and connect
- Get the containing folder reference through below code:
ITaskFolder containingFolder = taskService.GetFolder(folderPath);
- Call DeleteTask() with a task name
containingFolder.DeleteTask(taskName, 0);
This is how a complete function may Look like:
public bool DeleteTask(string taskPath) { ITaskService taskService = new TaskScheduler();taskService.Connect();ValidateTaskPath(taskPath); int lastIndex = taskPath.LastIndexOf(“\\”); string folderPath = taskPath.Substring(0, lastIndex); if (string.IsNullOrWhiteSpace(folderPath)) { folderPath = “\\”; } string taskName = taskPath.Substring(lastIndex + 1); try { ITaskFolder containingFolder = taskService.GetFolder(folderPath); containingFolder.DeleteTask(taskName, 0); } catch(FileNotFoundException exception) { throw new InvalidTaskPath(“Task Path is invalid”, exception); } return true; }
How to create\update a Scheduled task?
Creating a Scheduled task is not as easy as other operations. Before i detail out the steps here, i am pinpointing few things which are expected by COM and it’s behavior, to get our new task registered successfully in the system.
- Name of a new Task to be created can be empty while doing registration. If this is the case, system will generate a GUID and assign this as a name of a task
- The ExecutionTimeLimit, IdleDuration parameters under ITaskDefinition [see “Possible error and exceptions” section below] should be in a particular format. If this is not the case, new task won’t be registered
- Atleast one action should be specified when registering a new task
- If a task with the same name under the same folder exists, it will be updated if you register a task with a flag: “CreateOrUpdate” has a value of ‘6’. It is the 3rd argument passed in a call to RegisterTaskDefinition()
Now, let’s see how can we achieve this programmatically using C#.NET.
The below code will create\update a task under a Root Folder “\”. You may choose to create under a sub-folder.
Steps to create a task under Root Folder:
-
- Instantiate a task service and connect
ITaskService taskService = new TaskScheduler();
- Create a new task using task Service object. This requires calling “taskService.NewTask(0)”
ITaskDefinition taskDefinition = taskService.NewTask(0);
- Configure a new Task – all desirable properties like – Name, Description, Triggers, Actions, Author etc.
taskDefinition.Settings.Enabled = “true or false”;
taskDefinition.RegistrationInfo.Description = "task Description Goes Here";
taskDefinition.RegistrationInfo.Author = "task Author domainName\userName goes here";
taskDefinition.Settings.Hidden = “true or false”;
taskDefinition.Settings.Compatibility = _TASK_COMPATIBILITY.TASK_COMPATIBILITY_V2_1;if (“You want to set the execution time limit for Task Actions”)
{
TimeSpan timespan = TimeSpan.FromMinutes(“No of minutes goes here”);// this is the format needed by COM
taskDefinition.Settings.ExecutionTimeLimit = XmlConvert.ToString(timespan);
}if (“You want to allow the task to execute only when system is idle for so many minutes”)
{
taskDefinition.Settings.RunOnlyIfIdle = “true or false”;TimeSpan timespan = TimeSpan.FromMinutes(“Number of Minutes Goes Here”);
// this is the format needed by COM
taskDefinition.Settings.IdleSettings.IdleDuration = XmlConvert.ToString(timespan);
} - Configure triggers, if any
if (“You want this trigger to expire after sometime”)
ITriggerCollection triggers = taskDefinition.Triggers;
ITrigger trigger = triggers.Create("type of Trigger goes here"); // _TASK_TRIGGER_TYPE2 enumeration
trigger.Enabled = "true or false";
trigger.StartBoundary = DateTime.Now.ToString(Constants.DateTimeFormatExpectedByCOM);
{
trigger.EndBoundary = DateTime.Now.EndTime.ToString(Constants.DateTimeFormatExpectedByCOM);
} - Configure Actions [atleast one]
switch (actionType)
IActionCollection actions = taskDefinition.Actions;
_TASK_ACTION_TYPE actionType = "type of action you want goes here";
IAction action = actions.Create(actionType);
{
case _TASK_ACTION_TYPE.TASK_ACTION_EXEC:
IExecAction execAction = action as IExecAction;execAction.Path = “Path to Program To Run goes here”;
execAction.Arguments = “Optional Arguments goes here when starting the above executable”;
break;case _TASK_ACTION_TYPE.TASK_ACTION_SEND_EMAIL:
IEmailAction mailAction = action as IEmailAction;mailAction.From = “Sender email address goes here”;
mailAction.To = “Receiver email address goes here”;
mailAction.Server = “SMTPServer address goes here”;
mailAction.Subject = “email Subject goes here” ;
mailAction.Body = “email MessageBody goes here”;break;
case _TASK_ACTION_TYPE.TASK_ACTION_SHOW_MESSAGE:
IShowMessageAction showAction = action as IShowMessageAction;showAction.Title = “Display Title goes here”;
showAction.MessageBody = “Display Message goes here”;
break;
} - Till this point, we have configured task settings, triggers, actions, conditions. Now we need to register this task definition under some folder, RootFolder – “\”, here
// ‘6’ as argument means this task can be created or updated [“CreateOrUpdate” flag]
// creating this task in the root Folder
// Create SubFolder under RootFolder, if you require
ITaskFolder rootFolder = taskService.GetFolder(@"\");
// if Name id empty or null, System will create a task with name as GUID
rootFolder.RegisterTaskDefinition(task.Name, definition, 6, null, null, _TASK_LOGON_TYPE.TASK_LOGON_NONE, null);
- Instantiate a task service and connect
That’s all we need to do. If luck goes with you:), you won’t get errors [not like me]. Just kidding.
If you get a new error, contact me. I’ll try to find a cause and suggest you a resolution.
Possible errors and exceptions
- Error Code :-2147216616,
System.Runtime.InteropServices.COMException (0x80041318): (8,44):StartBoundary:26-01-2012 08:28:31
Cause: “ExecutionTimeLimit”/ “IdleDuration” are expected in some format by COM and not just simple ToString() conversion
Resolution: Use System.Xml.XmlConvert.ToString(). [Spent lot of time to get to this]
- System.Runtime.InteropServices.COMException (0x80041319): (38,4):Actions:
Cause: This means tat atleast one action should be specified when registering a task definition for a new task.
Simply put, a task should define atleast one action
Resolution: Specify atleast one action – be it : To send an email or To execute a task or To display a message
- System.Runtime.InteropServices.InvalidComObjectException was caught
Message=COM object that has been separated from its underlying RCW cannot be used.
Source=mscorlib
StackTrace:
at System.StubHelpers.StubHelpers.StubRegisterRCW(Object pThis, IntPtr pThread)
at TaskScheduler.ITaskService.get_Connected()InnerException:
Cause:
You are trying to work on the Released TaskScheduler COM object
Resolution:
Create a new object of TaskScheduler , connect and carry on with your task operation
- Error Code: -2147221163. Interface not registered while creating a task.
Yet to identify the real cause and resolution. [Noticed this when i share the same TaskScheduler instance and use it from diferent win forms]
References
I am getting below error “‘TaskScheduler’ is a ‘namespace’ but is used like a ‘type’ on the line
ITaskService taskService = new TaskScheduler();
Any ideas ?, I have referenced TaskScheduler in the project, I am using VS2010. Please help.
LikeLike
Check your project and solution settings. Which framework and CPU architecture are you targetting? Try to change those and help others by posting what you tried and what worked and what did not work
LikeLike
I had the same issue.
The problem is that the type name “TaskScheduler” is also the name of the namespace. Try using:
ITaskService taskService = new TaskScheduler.TaskScheduler();
That worked for me.
LikeLike
Just change the right hand side of the assignment to:
= new TaskScheduler.TaskScheduler();
LikeLike
I have a task schedule in Windows Task Scheduler..
I want to run my task schedule (Without create a new Task or Update Task, Just running the task),,
How can I run My Task from C#.Net? (Without use Process.Start())
can you help me?
LikeLike
The Scheduler WinForms Control provides a variety of Outlook style user interfaces and is often used together with the Reminder Control and Desktop Alert Control, which offers large amounts of scheduling UI to any Windows Forms application.
LikeLike
The .NET Scheduler Control is a lightweight UI component and support of customizing the Scheduler Control to create demand scheduling UI application.
LikeLike
We use an open source .NET wrapper around the COM interfaces that makes it much easier to use and it is portable across the V1 and V2 versions to support multiple OS’s. Check out http://taskscheduler.codeplex.com/
LikeLike
I looked at it. Basically, they did it manually which i have done by adding a reference. Exporting the Com interfaces.
LikeLike
I’m getting the following error at ITaskFolder rootFolder = taskService.GetFolder(@”\”);
This operation is supported only when you are connected to the server
LikeLike
Hello Selvi,
Are you able to make a successful call to Connect() on a taskService object?
LikeLike
NO.
Instead of TaskService, I have used TaskSchedulerClass..
How to use that TaskService class .
What is the namespace we need to use for that . Because i do not get that class in this.
LikeLike
I have been struggling for quite some time to programmatically get the existing tasks trigger(s), i.e. when the trigger actually runs, i dont care about next or last run times. So far I tried the following:
1) PowerShellPack Taskscheduler – works great except there is no function to build to extract the Trigger schedule
2) schtasks – only works for 2003, and it gives the exact format I want for the trigger schedule, e.g. “At 9:00 PM every day – After triggered, repeat every 1 hour for the duration of the 1 day”. Unfortunately win2k8 or win7 dont give the schedule because it no longer in recognizable format, which of course make sense… so I move on to my next attempt #3
3) C# Task Scheduler Library – I can get as far as the start boundary of the trigger, but that is about it… I am a little stuck, not sure if that is even possible…
Please help, at least let me know if this is even possible… I mean the win2k8 or win7 task scheduler snap in must have a function or something where it builds the schedule trigger from the xml data… maybe i need to find a function pointer to that and call it, but that is beyond my skillset.
LikeLike
Just in case anyone else was wondering there is a task scheduler wrapper for the native windows .dlls in codeplex for C#. It can even be used in Powershell by loading the dll through a [Reflection.Assembly]::LoadFile(). Anyway it gets me exactly what I needed.
LikeLike
Hi,
Thanks for this code.it had helped me alot.
i have a problem when i created a setup using this code.
when i run this code using visual studio it runs perfectly creates all schedule action that i require.
Bu t when i created a Windows setup wizard and tried to create a schedule a task then it fails with below error:
(47,4):Logon Type
Can you pls help me in resolving this error.
LikeLike
Can you please tell me repro steps here as well as how did you create a Windows Setup wizard
LikeLike
Hi,
I want to schedule some batch files to run on schedule time so i used your code .
i have created a exe which will schedule these batch files .When i separately call these exe its creating the task without any error.
I have created a Windows Setup Wizard .In this wizard i called the exe for creating a schedule task.when is exe is started during installation it doesn’t create any task and gives error.
I have used below code for Creating a schedule task:
rootFolder.RegisterTaskDefinition(taskname, taskDefinition, 6, null, null, _TASK_LOGON_TYPE.TASK_LOGON_NONE, null);
i think there is error due to “_TASK_LOGON_TYPE.TASK_LOGON_NONE”.
can u tel me which type should i use to Create Schedule task when i run this exe on some other machine.
LikeLike
Various options are:
public enum _TASK_LOGON_TYPE
{
TASK_LOGON_NONE = 0,
TASK_LOGON_PASSWORD = 1,
TASK_LOGON_S4U = 2,
TASK_LOGON_INTERACTIVE_TOKEN = 3,
TASK_LOGON_GROUP = 4,
TASK_LOGON_SERVICE_ACCOUNT = 5,
TASK_LOGON_INTERACTIVE_TOKEN_OR_PASSWORD = 6,
}
LikeLike
Hi,
i am getting following error when i am trying to schedule cron on Windows XP.
can u pls tell me how can i schedule a cron on windows XP .
LikeLike