views:

252

answers:

3

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
+1  A: 

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
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
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
thanks Maxim - note my question in the other answer that starts with "Oh...if I run with..." - any feedback on this welcome
Greg
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
+1  A: 

If you don't want a Windows service then starting the application with windows i 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? s 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 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.

Shoban
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
I have edited my question. please check.
Shoban
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
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
@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
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
Shoban
Greg
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
Actually might be better if I start a new question for this...
Greg
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
yep saw it now. Should I post the comment as answer with bit more explanation? :)
Shoban
if you like Shoban - otherwise someone's already responded suggested the shared library code approach
Greg
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
+3  A: 

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
thansk Maxim - any idea how this comparers with using the Task Scheduler Managed Wrapper that Shoban suggested? http://taskscheduler.codeplex.com/
Greg
Maxim Zaslavsky