如何在WinForms应用程序中安排任务?

7

问题:如何在WinForms应用程序中安排任务?这要么是(a)最佳方法/使用的.NET类/方法,要么是(b)如果有一个开源组件做得很好,哪个会被推荐。

背景:

  • Winforms应用程序(.NET v3.5,C#,VS2008)
  • 我假设我将始终运行winforms应用程序,并在不使用时最小化到系统托盘
  • 希望采用简单的方法(不想涉及单独运行的服务,该UI winforms应用程序与之通信等)
  • 希望能够让用户选择定期安排同步的频率(例如,每小时,每天-选择时间等)
  • 在调度程序触发运行代码块的时间上具有能力(假设它可以作为后台工作程序任务进行包装)
  • 应用程序始终在运行并出现在系统托盘中
3个回答

10
实际上,Windows 中有一个直接内置的工具可以做到这一点。它被称为“Windows 任务计划程序”!与其拥有一个 Windows 应用程序,坐等合适的时间运行代码片段,不如使用底层系统实用程序并将要运行的代码片段存储在一个单独的可执行文件中:这样更加“简单”和“高效”。
我以前使用过任务计划程序来配置我的应用程序以按照相当特定的时间表启动。从 .NET 应用程序中实现最好的方法是“使用这个方便的小库”。
基本上,要完成你在问题中所述的内容,需要制作一个提供 GUI 的 Windows 应用程序。该 GUI 应该具有调节任务创建和修改的选项。任务应该启动你需要运行的代码(你应该将其存储在一个单独的可执行文件中,可能是作为一个透明的 WinForms 应用程序,因此被隐藏)。
以下是来自代码项目文章本身的一些代码,说明如何创建任务:
//Get a ScheduledTasks object for the local computer.
ScheduledTasks st = new ScheduledTasks();

// Create a task
Task t;
try {
    t = st.CreateTask("D checker");
} catch (ArgumentException) {
    Console.WriteLine("Task name already exists");
    return;
}

// Fill in the program info
t.ApplicationName = "chkdsk.exe";
t.Parameters = "d: /f";
t.Comment = "Checks and fixes errors on D: drive";

// Set the account under which the task should run.
t.SetAccountInformation(@"THEDOMAIN\TheUser", "HisPasswd");

// Declare that the system must have been idle for ten minutes before 
// the task will start
t.IdleWaitMinutes = 10;

// Allow the task to run for no more than 2 hours, 30 minutes.
t.MaxRunTime = new TimeSpan(2, 30, 0);

// Set priority to only run when system is idle.
t.Priority = System.Diagnostics.ProcessPriorityClass.Idle;

// Create a trigger to start the task every Sunday at 6:30 AM.
t.Triggers.Add(new WeeklyTrigger(6, 30, DaysOfTheWeek.Sunday));

// Save the changes that have been made.
t.Save();
// Close the task to release its COM resources.
t.Close();
// Dispose the ScheduledTasks to release its COM resources.
st.Dispose();

注意:对我来说,“优先级”选项从未起作用,总是导致应用程序崩溃。我建议您将其省略;通常,它并不会有太大的影响。

在文章页面上有更多的代码示例,其中一些显示如何更改任务的设置、列出所有已安排的任务等。


谢谢Maxim - 你有没有想过这与Shoban建议的使用任务计划程序托管包(http://taskscheduler.codeplex.com/)相比如何? - Greg
刚刚查看了一下,它似乎是更新的版本。此外,我不知道我建议的那个是否与任务计划程序2.0(Vista和Win7)兼容,但我会尝试一下。 - Maxim Zaslavsky

2
如果您不想要一个 Windows 服务,那么在 Windows 启动应用程序是一种选择。您可以使用以下代码来实现这一点。
Dim regKey As RegistryKey
regKey = Registry.CurrentUser.OpenSubKey("Software\Microsoft\Windows\CurrentVersion\Run", True)
regKey.SetValue(Application.ProductName, Application.ExecutablePath)
regKey.Close()

“你说的同步调度是什么意思?它是一个不同的服务吗?那么,你可以使用定时器,然后将用户设置存储在xml文件或数据库中。如果你想要一个简单的存储方式,那么你可以使用My.Settings
编辑:我认为唯一的方法是在应用程序启动时检查日期,然后定期检查日期。另一个选择是以编程方式使用任务计划程序。Task Scheduler Managed Wrapper是一个开源包装器,你可以尝试一下。”

PS. 我不确定Shoban是否知道有没有一个开源控件使用了Task Scheduler Managed Wrapper并且已经开发了一个简单的对话框/用户界面,让用户选择他们想要的计划? - Greg
哦...如果我使用任务计划程序托管包来运行,那么这意味着我需要将代码从一个WinForms应用程序中拆分出来,对吗?基本上,我有一些任务代码,可以(a)由用户在winforms应用程序中手动启动,也可以(b)由用户安排。最好的处理方式是什么?即如何使(a)winforms应用程序调用手动运行案例的代码,以及(b)任务调用它以进行安排?任务能否调用正在运行的WinForms应用程序以触发它? - Greg
我没听懂你的意思。你能再解释一下吗?你可以在同一个应用程序中同时拥有你的代码(用户选择和触发器)。 - Shoban
假设有一些业务代码称为DoX(),它运行并执行某些操作。我希望用户能够最大化WinForms应用程序(从系统托盘中),并具有手动“立即运行”此功能的能力。然后,他们还可以通过调度程序安排运行此任务。如果调度是WinForms应用程序的一部分,那么我就知道如何做到这一点,但如果我将其拆分为从Windows任务触发,则会有点困惑?设计如何改变以便从任务和在应用程序内手动触发相同的逻辑? - Greg
如果您喜欢Shoban,否则已经有人建议使用共享库代码方法。 - Greg
显示剩余7条评论

1

所以你想要运行预定义代码的任务在你的应用程序中而不是外部exe文件?

  • 创建一个带有系统托盘图标的WinForms应用程序。如果您使用ClickOnce进行部署,请将发布者名称设置为“Startup”,这样快捷方式就会安装在启动目录中。

  • 使用计时器检查计划并在需要时启动线程。将本地计划存储在XML文件中,以便可以使用LINQ轻松查询。

  • 您正在使用Background worker运行任务,因此已经在使用线程。

如果您想要使用Windows任务计划程序,那么有一个API可供使用。虽然不太优雅,但足够好用。


好的 - 那么像获取计时器设置为5分钟这样的东西,所以每5分钟你都要在自己的应用程序代码中进行检查,看看是否已经超过了下一个预定点?(也就是说,你不能直接在每天晚上11点警报我的代码,例如?) - Greg
1
使用计时器并不是很准确,因为它会以预定义的时间间隔进行检查。如果你想要更精确,请使用 Windows 任务计划程序:它有一个相当优雅的API包装器。 - Maxim Zaslavsky
谢谢Maxim - 注意我的另一个回答中以“哦...如果我运行...”开头的问题 - 对此的任何反馈都受欢迎。 - Greg
没错,他说的没错。如果你想要为精确执行时间安排任务,计时器将不会准确。但是任务计划程序无法处理程序内部的代码块,你必须将它们定义为自己的程序。这也在下面被指出了。 - BigChrisDiD

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接