We have this ETL/Datawarehouse project going on at work and it uses these windows scheduled tasks to go grab data out of web services and import them into our database. There's nothing very interesting about that particular process however, deployment is always frustratingly manual and annoying because you have to update these scheduled tasks and copy the console applications and then modify config files.
My first charter was to get every console app using the same file for connection strings. That's actually pretty easy, you just use <connectionStrings configSource="yourNewFile.config">
You can check that out over here:
http://stevenharman.net/blog/archive/2007/06/07/tip-put-connection-strings-in-their-own-configuration-file.aspx
You create one single file for the connection strings then you add it to each of the projects that use it by using 'add as link' in the add new item dialog box.
The second step was creating a windows installer with a custom action. That's a bit more tricky... I found the steps of how to do that
http://msdn.microsoft.com/en-us/library/d9k65z2d(v=VS.100).aspx#Y231
The last thing I did was have to learn how to use the scheduled task COM+ API... here's where I read about it:
http://www.infoq.com/news/2008/02/TaskScheduler
There are some references on MSDN about it... here's a short sample code that I created to get me started:
public void CreateRepeatingIntervalTask(string userId, string userPwd, string appPath,
string triggerStartDateUtcString, short triggerDaysInterval, TimeSpan repeatAfter, string taskName)
{
//interval doc: //The amount of time between each restart of the task. The format for this string is P<days>DT<hours>H<minutes>M<seconds>S //(for example, "PT5M" is 5 minutes, "PT1H" is 1 hour, and "PT20M" is 20 minutes). //The maximum time allowed is 31 days, and the minimum time allowed is 1 minute. if (repeatAfter.TotalMinutes < 1)
throw new ArgumentException("repeatAfter must be >= 1 minute");
if (repeatAfter.TotalDays > 31)
throw new ArgumentException("repeatAfter must be <= 31 days");
string repetitionMinutesString = string.Format("P{0}DT{1}H{2}M{3}S",
repeatAfter.Days, repeatAfter.Hours, repeatAfter.Minutes, repeatAfter.Seconds);
ITaskService scheduler = new TaskSchedulerClass();
scheduler.Connect(null, null, null, null);
ITaskDefinition task = scheduler.NewTask(0);
IExecAction execAction = (IExecAction)task.Actions.Create(_TASK_ACTION_TYPE.TASK_ACTION_EXEC);
execAction.Path = appPath;
IDailyTrigger daily = (IDailyTrigger)task.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_DAILY);
daily.StartBoundary = triggerStartDateUtcString;
//http://msdn.microsoft.com/en-us/library/aa381138(v=VS.85).aspx daily.DaysInterval = triggerDaysInterval;
daily.Repetition.Interval = repetitionMinutesString;
daily.Repetition.StopAtDurationEnd = false;
ITaskFolder folder = scheduler.GetFolder("\\");
IRegisteredTask regTask = folder.RegisterTaskDefinition(
taskName,
task,
(int)_TASK_CREATION.TASK_CREATE_OR_UPDATE,
userId,
userPwd,
_TASK_LOGON_TYPE.TASK_LOGON_INTERACTIVE_TOKEN_OR_PASSWORD,
"");
}