原文链接:http://yoursandmyideas.wordpress.com/2012/01/07/task-scheduler-in-c-net/
本文将讲解如何使用C#语言,以编程的方式来创建计划任务。事实上windows 操作系统已经提供了一套用户界面来完成诸如查看、添加,删除计划的任务的功能, 你可以使用compmgmt.msc命令来打开此窗口, 或者在开始菜单里搜索Task Scheduler。
首先,我们需要一个底层的库,可以是COM库 或者非托管C(C++)库 亦或者托管的被包装的直接与系统打交道的库。
啊啊啊啊。。。
不用担心!
给你个小技巧。
如果你敢(外国人的幽默,不懂!)浏览C:\Windows\syswow64目录,你就会注意到Taskschd.dll 程序集。 这就是我们所需要的!
步骤:1. 使用VS创建一个新项目
2. 对项目右键添加引用, 选择 “TaskScheduler TypeLibrary”
最后一步将会产生一个互操作程序集并且被引用到项目中(The last step resulted in an Interop assembly generated and referenced in the project)。 你可以右键查看此引用的属性来确认这一点。
下面, 我们就使用此程序集中定义/导出的类型来完成 --
- 查看所有计划任务
- 以编程的方式添加一个计划任务
- 删除一个计划任务
所有CRUD(译者注:create,read,update,delete) 操作,我们都将通过 ITaskService 类型来实现,但首先我们需要把 ITaskService 连接到一个实际存在的用来存储计划任务的库(store)。 我们将实例化 TaskService 并且调用 Connect 方法来连接。
在真正开始之前,我们还需要了解一下计划任务管理的方式 --
所有计划任务都是以文件夹层次的形式来管理的, 就是说每一个任务都会存在于某一个文件夹下。最最顶层的文件夹被称为 根目录(Root Folder), 路径为 @“\” 或者“\\”。 每一个文件夹都可以有子文件夹。
下面, 来详细讲解。
如何获取所有计划任务
1. 实例化 TaskService 对象
ITaskService taskService = new TaskScheduler();
2. 调用之前创建对象的 Connect() 方法
taskService.Connect();
3. 获取根目录的引用
ITaskFolder rootFolder = taskService.GetFolder(@"\");
记住,根目录的路径是“\\”
4. 调用文件夹的 GetTasks() 方法
IRegisteredTaskCollection tasks = rootFolder.GetTasks(0);//0 or 1: 1 will include all hidden tasks as well
注意:这种方式只能获取直接位于当前文件夹的Task, 如果想要获取所有的包括子文件夹下面的task, 就需要循环调用子文件夹的 GetTasks() 方法。
private voidLoad()
{
ITaskServicetaskService =new TaskScheduler();
taskService.Connect();
List<IRegisteredTask> tasks =newList<IRegisteredTask>();
// “\\” or @”\” is the RootFolder
this.GetData(taskService.GetFolder(@”\”), tasks);
this.view.ScheduledTasks = tasks;
}
private voidGetData(ITaskFolder folder,List tasks)
{
foreach(IRegisteredTask taskinfolder.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);
}
如何删除计划任务
我们需要task的一些详细信息,像任务名称、所在文件夹。一般来说,每一个任务的 Location 属性都会包含这些信息。
例如 “\\SampleTaskFolder\\SampleTask”, 意味着, Task Name = SampleTask, 在SampleTaskFolder 文件夹下面。
在知道全路径的情况下删除Task的步骤:
ITaskFolder containingFolder = taskService.GetFolder(folderPath);
containingFolder.DeleteTask(taskName, 0);
完整代码--
public bool DeleteTask(stringtaskPath)
{
ITaskService taskService = new TaskScheduler();
taskService.Connect();
ValidateTaskPath(taskPath);
intlastIndex = taskPath.LastIndexOf(“\\”);
stringfolderPath = taskPath.Substring(0, lastIndex);
If(string.IsNullOrWhiteSpace(folderPath))
{
folderPath =“\\”;
}
stringtaskName = taskPath.Substring(lastIndex + 1);
try
{
ITaskFolder containingFolder = taskService.GetFolder(folderPath);
containingFolder.DeleteTask(taskName, 0);
}
catch(FileNotFoundException exception)
{
thrownewInvalidTaskPath(“Task Path is invalid”, exception);
}
return true;
}
如何创建/删除计划任务
创建就不是那么简单了。下面列出几点需要注意的事情 --
ITaskDefinition 的 ExecutionTimeLimit,IdleDuration 属性必须传特定的格式。如果不这样做的话会失败【查看本文 “可能的错误和异常”模块】。
必须指定至少一个 Action
如果当调用 RegisterTaskDefinition() 来注册任务的时候, 第三个参数传入的是 “CreateOrUpdate” 即 ”6“ 会更新现有的同名的任务,如果有的话。
在一个特定文件夹下注册任务的步骤:
ITaskService taskService =newTaskScheduler();
taskService.Connect();
ITaskDefinition taskDefinition = taskService.NewTask(0);
taskDefinition.RegistrationInfo.Description = "task Description Goes Here";
taskDefinition.RegistrationInfo.Author = "task Author domainName\userName goes here";taskDefinition.Settings.Enabled =“true or false”;
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);
}
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);
if (“You want this trigger to expire after sometime”)配置 Action [至少一个]
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;
}
// creating this task in the root Folder
// Create SubFolder under RootFolder, if you require
ITaskFolder rootFolder = taskService.GetFolder(@"\");
//’6′ as argument means this task can be created or updated ["CreateOrUpdate" flag]要做的就这些了,如果你运气好的话, 你应该不会出任何错误【我就遇到很多】。 呵呵, 开玩笑。。。
如果你遇到可一个新问题,联系我【原作者】,我会尝试找出原因并给你一个解决方案。
原因: “ExecutionTimeLimit”/ “IdleDuration” 格式错误,不是简单的ToString。
方案: 使用 System.Xml.XmlConvert.ToString(). [花了我好多时间才知道的]
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:
原因:使用已经释放的 TaskScheduler COM 对象
Error Code: -2147221163. Interface not registered while creating a task.
原因: 未知。[当我在不同的win form 窗体中使用同一个 TaskScheduler 实例的时候就会发生]
1)我在使用的时候,感觉设置那些setting和RegistrationInfo是最麻烦的事情, 最后我发现, 在操作系统提供的界面中通过UI创建好我们需要的task之后,导出xml, 你会发现xml中的tag 的名称和taskDefinition的相关属性的名称是一样的,可以参照着设置!
2)ITaskFolder 实例 有另一个方法叫做 RegisterTask, 可以接受一个xml字符串来创建Task, 也就是说我们把导出的xml读进来直接传到RegisterTask 也是可以的!!