Heute möchten wir durch ein C#-Programm automatisch den Task-Planer (Schedueld Task) nutzen, um unsere zuvor erstellten Twitter-Nachrichten zeitversetzt an unser Twitter-Account zu senden. Wir könnten selbstverständliche ein eigenes Programm mit einem Timer schreiben, der zum vordefinierten Zeitpunkt die Twitter-Nachricht sendet. Doch diese wäre eine weitere Anwendung, die ständig auf dem Rechner laufen müsste. Selbst professionelle Anwendungen wie beispielsweise Pinnacles WIN PVR gehen bei der Aufnahme des TV-Programmes inzwischen dazu über, den Windows-eigenen Scheduler zu nutzen. Dies spart Ressourcen und vereinfacht die möglichen Fehlerquellen. Auf einem Linux-System käme schließlich auch niemand auf die Idee, die Crontab nachzubauen …
Unser heutiges Ziel ist, vorhandene WordPress-Artikel über die folgenden Tage verteilt als Tweet zu senden. Hierzu haben wir bereits die WordPress-Tabelle „wp_posts“ von Revisionen befreit und eine Tabelle angelegt, die lediglich die Felder „post_title“ und die „guid“ enthält. Die „Guid“ ist der Link auf die ID in WordPress. Dies ist nicht der SEO-optimierte Link. Da wir sowie einen URL-Verkürzer nutzen, benötigen wir für die Optik keinen lesbaren Link. Suchmaschinen wie Google lieben lesbare Links. Diese werde ich ein wenig enttäuschen. Ich spare mir Zeit und Aufwand. Zumal WordPress von alleine diesen Link per 301 Redirect auf den SEO-optimierten Link auflöst.
Wir erstellen eine weitere SQL-Tabelle, die lediglich eine ID, ein Statusfeld (Tinyint, Default 0) und ein Textfeld enthält. Diese nenne ich „tw_sirmark“. In dieser Tabelle speichere ich die Tweets. Eine eindeutige ID erleichtert das Auffinden der Tweet-Nachricht. Das Statusfeld gibt Auskunft darüber, ob der Tweet „geplant“ oder bereits veröffentlicht wurde.
Scheduled Task: Erstellen und Löschen von Tasks im Task-Planer
Nun schreiten wir zum eigentlichen Thema von Heute: Die Scheduled Task. Wir möchten mittels C#-Konsole für jeden geplanten Tweet einen Scheduled Task erstellen. Wenn dieser abgearbeitet ist, soll der Eintrag gelöscht werden. In unserem Beispiel ist der Weg sicherlich nicht optimal, denn aktuell sollen über 200 Einträge „getweetet“ werden. Doch in einem folgenden Projekt sollen lediglich vier bis fünf Einträge pro Woche generiert werden. So dient dieses Projekt als Vorlage. Ferner werden wir gleich sehen, wie sich der Windows-Scheduler mit einer solchen Menge an Einträgen „schlägt“.
Um den Windows-Task-Scheduler anzusprechen, gibt es viele Möglichkeiten. Mir persönlich sagt die DLL „Microsoft.Win32.TaskScheduler.dll“ am meisten zu. Diese ist über Codeplex kostenfrei erhältlich.
Über den folgenden Beispielcode erstellen wir unseren ersten Test-Task. Dieser ruft einfach testweise das Notepad auf.
static void Main(string[] args)
{
// Task Service auf der lokalen Maschine
using (TaskService ts = new TaskService())
{
// Task erstellen
TaskDefinition td = ts.NewTask();
//Beschreibung hinzufügen
td.RegistrationInfo.Description = "Meine Task-Beschreibung";
// Task Trigger: Starte täglich um 15 Uhr
td.Triggers.Add(new DailyTrigger {StartBoundary = DateTime.Today + TimeSpan.FromHours(15)});
// Zum Test das Notepad öffnen
td.Actions.Add(new ExecAction("notepad.exe", "", null));
// Register den Task im Root-Ordner
ts.RootFolder.RegisterTaskDefinition(@"Mein-Test-Trigger 15 Uhr", td);
}
}
Weitere Trigger-Einsatzzeiten:
//Task Trigger: Einmaliger Task, 1 Tag nach Erstellung, um 3 Uhr
// Für weitere Verfeinerung: new TimeSpan(hour, min, sec)
td.Triggers.Add(new TimeTrigger { StartBoundary = DateTime.Today + TimeSpan.FromDays(1) + TimeSpan.FromHours(3) });
//Task Trigger: Einmaliger Task am 12.07.2012 um 13:35:36 Uhr
td.Triggers.Add(new TimeTrigger { StartBoundary = new DateTime(2012, 7, 12, 13, 35, 36) });
Für unser Projekt nutzen wir den TimeTrigger. Über diesen können wir einen exakten Zeitpunkt bestimmen. Nun benötigen wir noch eine Möglichkeit, den Task-Trigger wieder zu löschen.
//Zugriff auf einen bestehenden Task
string taskName = "Mein-Test3";
using (TaskService ts = new TaskService())
{
Task t = ts.GetTask(taskName);
ts.RootFolder.DeleteTask(taskName);
}
Im obigen Beispiel benötigen wir den Namen des Task, den wir löschen möchten. Natürlich können wir dem per Task aufzurufenden Programm auch den Namen übergeben, damit dieser den abgelaufenen Task löschen kann. Besser ist es jedoch, nach abgelaufenen Tasks zu suchen und diese zu löschen. Vorsicht: Eventuell werden somit mehr Tasks gelöscht, als man vielleicht möchte. Da ich den Anfangsbestandteil des Tasknamen kenne („MeinTask1“,“MeinTask2“ -> „MeinTask%“), werde ich nicht nur nach abgelaufenen Tasks, sondern auch nach dem Namenbestandteil suchen, bevor ich den Task lösche.
Hinweis: Es kann einem Task selbstverständlich auch in den Setting mitgegeben werden, dass der Task sich nach Ausführung selbst löscht.
// Task löschen, wenn er nicht erneut geplant wird
td.Settings.DeleteExpiredTaskAfter = TimeSpan.FromMinutes(2);
// Task beenden nach ....
td.Settings.ExecutionTimeLimit = TimeSpan.FromMinutes(2);
Löschen von bestehenden Scheduled Task mit C#
Da wir den Task vom aufzurufenden Konsolenprogramm auch selbst löschen wollen, benötigen wir eine Möglichkeit, auf diesen zuzugreifen. Sonst würden alle abgelaufenen Tasks im Scheduler bestehen bleiben.
//Zugriff auf bestehende Scheduled-Tasks, Auslesen des Root-Ordners
TaskService taskService = new TaskService();
TaskFolder tasksFolder = taskService.GetFolder(@"");
TaskCollection listOfTasks = tasksFolder.Tasks;
foreach (Task theTask in listOfTasks)
{
//Zugriff auf den Namen aller bestehenden Tasks im Root-Order
string s = theTask.Name;
//Nächste Laufzeit: Abgelaufene Tasks zeigen: 01.01.0001 00:00:00
string sNext = theTask.NextRunTime.ToString();
}
Ändern von bestehenden Scheduled Tasks
//Ändern von bestehenden Tasks
//Zugriff auf bestehende Scheduled-Tasks, Auslesen des Root-Ordners
TaskService taskService = new TaskService();
TaskFolder tasksFolder = taskService.GetFolder(@"");
TaskCollection listOfTasks = tasksFolder.Tasks;
foreach (Task theTask in listOfTasks)
{
// Zugriff auf den Namen aller bestehenden Tasks im Root-Ordner
// Nur bestimmte Tasks auslesen
if (theTask.Name.StartsWith("Mein-"))
{
//Auslesen der Eigenschaften (Demo)
string sName = theTask.Name;
string sBeschreibung = theTask.Definition.RegistrationInfo.Description;
string sAusfuehrung = theTask.Definition.Triggers.ToString();
string sAction = theTask.Definition.Actions.ToString();
using (TaskService ts = new TaskService())
{
// Neuen Task erstellen
TaskDefinition td = ts.NewTask();
// Setzen der Werte, die sich ändern sollen
td.RegistrationInfo.Description = "Meine neue Task-Beschreibung";
// Task löschen, wenn er nicht erneut geplant wird
td.Settings.DeleteExpiredTaskAfter = TimeSpan.FromMinutes(2);
// Task beenden nach ....
td.Settings.ExecutionTimeLimit = TimeSpan.FromMinutes(2);
td.Actions.Add(new ExecAction(@sAction, "", null));
//RegisterTaskDefinition ändert den Task, wenn dieser bereits vorhanden ist
ts.RootFolder.RegisterTaskDefinition(@sName, td);
}
}
}
[tweet http://twitter.com/sirmark_de/status/220481183354601472]