7

QUESTION: How can I schedule tasks in a WinForms app? That is either (a) what is the best approach / .NET classes/methods to use of (b) if there is an open source component that does this well which one would be recommended.

BACKGROUND:

  • Winforms app (.NET v3.5, C#, VS2008)
  • I'm assuming I will run the winforms application always, and just minimise to the system tray when not in use
  • Want a simple approach (didn't want to get into separate service running that UI winforms app talks to etc)
  • Want to be able to let the user select how often to schedule the sync (e.g. hourly, daily - pick time, etc)
  • Ability to at the times when the scheduler fires to run a chunk of code (assume it could be wrapped as a backgroundworker task for example)
  • The application is always running & appears in the system tray
John Saunders
  • 160,644
  • 26
  • 247
  • 397
Greg
  • 34,042
  • 79
  • 253
  • 454

3 Answers3

10

There's actually something directly built into Windows that will do just that. It's called the Windows Task Scheduler! Rather than having a Windows application that sits and waits for the right time to run a piece of code, you'd be better off just using an underlying system utility and storing the piece of code to run in a separate executable: it's easier and more efficient.

I've used the Task Scheduler before to configure my applications to start on a pretty specific schedule. The best way to do it out of a .NET application is to use this handy little library.

Basically, to accomplish what you've stated in your question, you need to make a Windows application that provides a GUI. This GUI should have options that regulate the creation and alteration of a Task. The Task should launch the code you have to run (you should store it in a separate executable, probably as a WinForms app that's transparent and, thus, hidden.)

Here's some code from the CodeProject article of the library itself that illustrates how to create a task:

//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();

NOTE: The priority option never worked for me, always crashing the app. I recommend you leave it out; usually, it doesn't make that big a difference.

There are more code samples on the article page, some of which show how to change the settings of a Task, list all Scheduled Tasks, etc.

Maxim Zaslavsky
  • 17,787
  • 30
  • 107
  • 173
  • thansk Maxim - any idea how this comparers with using the Task Scheduler Managed Wrapper that Shoban suggested? http://taskscheduler.codeplex.com/ – Greg Mar 22 '10 at 05:28
  • Just checked that one out, it seems to be newer. Also, I don't know if the one I suggested works with Task Scheduler 2.0 (Vista & Win7), but I'll try it. – Maxim Zaslavsky Mar 22 '10 at 05:42
2

If you don't want a Windows service then starting the application with windows is an option. You can use the following code to do that.

Dim regKey As RegistryKey
regKey = Registry.CurrentUser.OpenSubKey("Software\Microsoft\Windows\CurrentVersion\Run", True)
regKey.SetValue(Application.ProductName, Application.ExecutablePath)
regKey.Close()

What did you mean by schedule the sync? Is it a diff service? Then you can use the timer and then store the user settings in an xml file or a DB. If you want a simple storage then you can use My.Settings.

Edit: I think the only way is to check for the date when the app starts and then check the date periodically. Another option is to use the task scheduler programatically. Task Scheduler Managed Wrapper is an open source wrapper which you can try out.

Xupi
  • 92
  • 1
  • 9
Shoban
  • 22,920
  • 8
  • 63
  • 107
  • Sorry, my use of "sync" just complicates things. It's just a windows application that the user could schedule a "task" to be run periodically. I have a database so can store data there. So use of the Timer class seems to be the way to go, but you have to do the day/time checks yourself in your code then? (as timer doesn't really have any calendar based scheduling build in it seems) – Greg Mar 22 '10 at 04:45
  • are yes...this (i.e. Task Scheduler Managed Wrapper) was the kind of thing I was fishing for...do you think this is robust approach? In ohter words would it be more robust/safe to just use the Timer and write the date/time checking code in your application? By robust I guess I mean can I assume that it would work on any PC without any gottchas (alberit XP, or vista or Windows 7 etc) – Greg Mar 22 '10 at 05:02
  • PS. I don't suppose Shoban you know of an open source control that uses Task Scheduler Managed Wrapper that has a simple dialog/UI already developed to let a user select the schedule they want? – Greg Mar 22 '10 at 05:06
  • @Greg .. no no idea about controls which does this ;-) sorry. Will post if I get any. Yes it will work right from win xp. – Shoban Mar 22 '10 at 05:13
  • Oh...if I run with Task Scheduler Managed Wrapper however this implies I would have to split my code out from the one WinForms application no? Basically I have task code that can be (a) kicked off manually by the user in the winforms app, and (b) scheduled by the user. What would be the best way to handle this? i.e. how would the (a) winforms app call the code for the manually run case and (b) the task call it for the scheduled case? Can a task call into a running WinForms application to trigger it? – Greg Mar 22 '10 at 05:39
  • I didnt get you. Can you explain a bit. You can have both your code (user choice & trigger) in the same app. – Shoban Mar 22 '10 at 05:44
  • So lets say I have some business code call DoX() that runs and does stuff. I want the ability for the user to maximise the WinForms application (from System Tray) and have the ability to manually "Run Now" this function. THEN also they have the ability to schedule this task to run via the scheduler. If the scheduling was part of the WinForms application then I would know how to do this, BUT if I break it out to triggering from Windows Tasks then I get a bit lost? How would the design have to change to trigger this same lgoic from both the Task & Manually within the app? – Greg Mar 22 '10 at 06:03
  • ah! ok.... now i see the complication.I thought you will be running other apps from your app. But now I think timer is the only way for this. otherwise what you can do is add arguments to your app and use thse arguments to run a specific piece of code. Then use task scheduler to run this app (with the arguments) like for example. yourapp.exe -a will run 1 piece of code. – Shoban Mar 22 '10 at 06:29
  • Raise the question at: http://stackoverflow.com/questions/2490389/how-can-i-run-some-common-code-from-both-a-scheduled-via-windows-task-b-man – Greg Mar 22 '10 at 06:39
  • yep saw it now. Should I post the comment as answer with bit more explanation? :) – Shoban Mar 22 '10 at 06:41
  • if you like Shoban - otherwise someone's already responded suggested the shared library code approach – Greg Mar 22 '10 at 06:58
  • Shoban's right, you should use a parameterized task (e.g. `yourapp.exe -a`) for running the business logic that you're trying to schedule. Another question that you wrote in the comments here, @Greg, is whether it's possible to create a Task and then launch it right when the user clicks the Run Now button. Yes, this is possible: basically, you're just running the task! You could either the .job path of the task and run it as a process, or, if your business logic is in the main scheduling WinForms app, you can just run the method automatically. – Maxim Zaslavsky Mar 22 '10 at 23:45
1

So you want to run tasks that are predefined code in your app and not external exes?

  • Make a winforms app with a system tray icon. If you are deploying using click once, make the Publisher name "Startup" so a shortcut installs in the Startup directory.

  • Use a timer to check against the schedule and launch threads if needed. Store you local schedule in an XML file so it can be easily queried with LINQ.

  • You are running your tasks with Background worker so threading is already in use.

If you wanted to use the Windows Task Scheduler however, there is an API for it. Not a graceful module but it works well enough.

BigChrisDiD
  • 152
  • 5
  • ok - so something like get the Timer to be set for 5 minutes, so every 5 minutes you do a check yourself (within your own application code) to see whether you have crossed a the next scheduled point? (i.e. so you can't say directly alert my code every day at 11pm for example?) – Greg Mar 22 '10 at 04:43
  • 1
    Using a Timer isn't that accurate, because you're checking at predefined intervals. If you want to be precise, use the Windows Task Scheduler: there's a pretty graceful API _wrapper_ for it. – Maxim Zaslavsky Mar 22 '10 at 05:25
  • thanks Maxim - note my question in the other answer that starts with "Oh...if I run with..." - any feedback on this welcome – Greg Mar 22 '10 at 05:40
  • Yep, what he said. A Timer wouldn't be accurate at all if you wanted to schedule your tasks for a precise execution time. But Task Scheduler won't work with code blocks inside your program, you'd have to define them all as their own program. Which has also been pointed out below. – BigChrisDiD Mar 23 '10 at 03:47