Task Scheduler in C#.Net

原文链接:http://yoursandmyideas.wordpress.com/2012/01/07/task-scheduler-in-c-net/

 

本文将讲解如何使用C#语言,以编程的方式来创建计划任务。事实上windows 操作系统已经提供了一套用户界面来完成诸如查看、添加,删除计划的任务的功能, 你可以使用compmgmt.msc命令来打开此窗口, 或者在开始菜单里搜索Task Scheduler。

系统要求

Windows Vista 或者 Windows 7 或者更高版本的操作系统

开发环境

  • 开发IDE - Visual Studio
  • Task Scheduler COM Library

如何实现

首先,我们需要一个底层的库,可以是COM库 或者非托管C(C++)库 亦或者托管的被包装的直接与系统打交道的库。

啊啊啊啊。。。

不用担心!

给你个小技巧。

如果你敢(外国人的幽默,不懂!)浏览C:\Windows\syswow64目录,你就会注意到Taskschd.dll 程序集。 这就是我们所需要的!

步骤:

1. 使用VS创建一个新项目

2. 对项目右键添加引用, 选择 “TaskScheduler TypeLibrary

Task Scheduler in C#.Net_第1张图片

最后一步将会产生一个互操作程序集并且被引用到项目中(The last step resulted in an Interop assembly generated and referenced in the project)。 你可以右键查看此引用的属性来确认这一点。

 

下面, 我们就使用此程序集中定义/导出的类型来完成 --

- 查看所有计划任务

- 以编程的方式添加一个计划任务

- 删除一个计划任务

 

所有CRUD(译者注:create,read,update,delete) 操作,我们都将通过 ITaskService 类型来实现,但首先我们需要把 ITaskService 连接到一个实际存在的用来存储计划任务的库(store)。 我们将实例化 TaskService 并且调用 Connect 方法来连接。

 

在真正开始之前,我们还需要了解一下计划任务管理的方式 --

Task Scheduler in C#.Net_第2张图片

所有计划任务都是以文件夹层次的形式来管理的, 就是说每一个任务都会存在于某一个文件夹下。最最顶层的文件夹被称为 根目录(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;
}

 

如何创建/删除计划任务

 

创建就不是那么简单了。下面列出几点需要注意的事情 --

  • 注册任务的时候,任务名称可以为空,如果这样做的话,系统会生成一个GUID作为任务名称
  • ITaskDefinition 的 ExecutionTimeLimit,IdleDuration 属性必须传特定的格式。如果不这样做的话会失败【查看本文 “可能的错误和异常”模块】。

  • 必须指定至少一个 Action

  • 如果当调用 RegisterTaskDefinition() 来注册任务的时候, 第三个参数传入的是 “CreateOrUpdate” 即 ”6“ 会更新现有的同名的任务,如果有的话。

在一个特定文件夹下注册任务的步骤:

  1. 实例化 task service 并连接t
     ITaskService taskService =newTaskScheduler();

     taskService.Connect();

  2. 创建 TaskDefinition 对象
    ITaskDefinition taskDefinition = taskService.NewTask(0);
  3. 配置所有必须的属性– Name, Description, Triggers, Actions, Author etc.

    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);
     }

  4. 配置triggers, 如果需要
    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”)
    {
            trigger.EndBoundary = DateTime.Now.EndTime.ToString(Constants.DateTimeFormatExpectedByCOM);
    }
  5. 配置 Action [至少一个]
    IActionCollection actions = taskDefinition.Actions;
    _TASK_ACTION_TYPE actionType = "type of action you want goes here";
    IAction action = actions.Create(actionType);
    switch(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;
    }

  6. 到此, 我们已经配置了 task settings, triggers, actions, conditions. 接下来我们要把这个task注册到某个文件夹下RootFolder – “\”
    // 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]
    // 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);

 要做的就这些了,如果你运气好的话, 你应该不会出任何错误【我就遇到很多】。 呵呵, 开玩笑。。。

如果你遇到可一个新问题,联系我【原作者】,我会尝试找出原因并给你一个解决方案。

可能的错误和异常

  • Error Code :-2147216616,
    System.Runtime.InteropServices.COMException (0×80041318): (8,44):StartBoundary:26-01-2012 08:28:31

                 原因: “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 也是可以的!!

 

你可能感兴趣的:(C#,Accumulation)