Task scheduler vel. harmonogram zadań
kubryk
Witam!
Jak dodać zadanie do schedulera?
Jak napisać program, żeby się sam uruchamiał?
Jak z poziomu Delphi utworzyć plik .job?
Widzę, że dość dużo pytań tego typu przewija się (najczęściej bez odpowiedzi niestety) przez grupy i różne forum. Tak więc wychodząc na przeciw przyszłym problemom, opisze jak z tym sobie poradzić.
Po pierwsze musimy zassać z sieci bibliotekę MsTask, która zawiera wszystkie potrzebne nam rzeczy. Znajdziemy ją pod adresem:
http://codecentral.borland.com/codecentral/ccweb.exe/listing?id=16007
Później utworzymy unit, zawierający potrzebne nam procedurki. Aby było szybko, łatwo i przyjemnie, cały kod zamieszczam poniżej:
unit SchedulerUnit;
interface
Uses MsTask, Windows, ActiveX, SysUtils;
function StrToWide(SourceString : String): LPCWSTR;
procedure AddToScheduler(ApplicationPath : String; Hour : String);
implementation
var
wsBuffer : PWideChar;
SchedulingAgent : ITaskScheduler;
Task : ITask;
function StrToWide(SourceString : String): LPCWSTR;
begin
Result := StringToWideChar(SourceString, wsBuffer, Length(SourceString) + 1);
end;
procedure AddToScheduler(ApplicationPath : String; Hour : String);
var
pIPersistFile : IPersistFile;
TaskName : String;
WorkItem : IUnknown;
piNewTrigger : Word;
ITTrigger : ITaskTrigger;
TaskTrig : TTaskTrigger;
uname, passwd : PWideChar;
begin
GetMem(wsBuffer, 1024);
// Inicjowanie schedulera;
if not assigned(SchedulingAgent) then
begin
ActiveX.CoInitialize(nil);
ActiveX.CoCreateInstance(CLSID_CSchedulingAgent, nil, CLSCTX_INPROC_SERVER, IID_ITaskScheduler, SchedulingAgent);
end;
TaskName := 'MojeZadanie'; //nazwa zadania
SchedulingAgent.Delete(StrToWide(TaskName)); //kasowanie, żeby zabezpieczyć się przed tworzeniem istniejącego zadania
SchedulingAgent.NewWorkItem(StrToWide(TaskName), CLSID_CTask, IID_IScheduledWorkItem, WorkItem); //utworzenie zadania
Task := ITask(WorkItem);
Task.SetApplicationName(StrToWide(ApplicationPath));
Task.SetWorkingDirectory(StrToWide(ExtractFilePath(ApplicationPath));
GetMem(uname, 255);
GetMem(passw, 255);
StringToWideChar(User, uname, Length(User) + 1);
StringToWideChar(Pass, passw, Length(Pass) + 1);
Task.SetAccountInformation(uname, passw); //(User, Password) Ustawnienie konta usera
Task.CreateTrigger(piNewTrigger, ITTrigger); //utworzenie triggera
ZeroMemory(@TaskTrig, sizeof(TASK_TRIGGER));
TaskTrig.cbTriggerSize := sizeof(TASK_TRIGGER);
TaskTrig.wBeginYear := 2004; //
TaskTrig.wBeginMonth := 7; // data rozpoczęcia - dowolna
TaskTrig.wBeginDay := 25; //
TaskTrig.wStartHour := StrToInt(Copy(Hour, 1, 2)); //godzina wykonania
TaskTrig.wStartMinute := StrToInt(Copy(Hour, 4, 2)); //minuty wykonania
TaskTrig.TriggerType := TASK_TIME_TRIGGER_DAILY; //typ triggera (dni)
TaskTrig.Type_.Daily.DaysInterval := 1; //ustawienie typu - codziennie
ITTrigger.SetTrigger(@TaskTrig); //zapisanie triggera
// Zapisywanie .job;
Task.QueryInterface(IID_IPersistFile, pIPersistFile);
if pIPersistFile <> nil then
pIPersistFile.Save(nil, true);
FreeMem(wsBuffer);
FreeMem(uname);
FreeMem(passw);
end;
end.
W tym konkretnym przykładzie zadanie będzie wykonywane codziennie o godzinie podanej w parametrze procedury (w postaci 'hh:mm').
Podobnie ścieżkę do programu podajemy w parametrze procedury. Więc przykładowe wywołanie będzie wyglądało nastęoująco:
AddToScheduler('c:\katalog\mojprogram.exe', '14:31');
W tym konkretnym przykładzie zadanie będzie wykonywane codziennie, jednak nic nie stoi na przeszkodze, aby robić to co tydzień, albo co dwa dni. Należy jedynie zmienić parametry TaskTrig.TriggerType.
Więcej informacji o tym znajdziecie na stronie MS pod adresem:
http://msdn.microsoft.com/library/en-us/taskschd/taskschd/trigger_type_union.asp
Wszystkie zadania w postaci plików *.job przechowywanie są w %WINDIR%\Tasks. W ten sposób też możemy sprawdzać, czy jakieś interesujące nas zadanie istnieje (za pomocą FileExists).
Tak po krótce przedstawia się sprawa planowania zadań w Windows. Oczywiście przedstawiłem tu tylko nieznaczną część możliwości (jednak tą najbardziej potrzebną) Schedulera. A po resztę odsyłam na wspomnianą wcześniej stronę MSDN.
Pozdrawiam i życzę owocnej pracy, Kuba Kubryński
co to znaczy "vel." :D
Ja poproszę następną porcję :) Ponadto proponowałbym umieszczenie w procedurze AddToScheduler() po procedurze GetMem() sekcji try, a wywołanie FreeMem() do sekcji finally. Nie wiem, czy się zgodzicie (mogą być ważne powody, dla których chcemy stracić parę bajtów :) ), ale takie jest moje skromne zdanie.
troche krótki ten artykuł. ale fajny
Na początku procedury "AddToScheduler" jest instrukcja:
// Inicjowanie schedulera;
if not assigned(SchedulingAgent) then
To sprawdzenie ma sens pod warunkiem, że zmienna "SchedulingAgent" jest zmienną globalną. Zmienne globalne są inicjowane pustymi wartościami. Natomiast zmienne lokalne mają wartości przypadkowe do czasu podstawienia pod nie pierwszej wartości. Poza tym wartości zmiennych lokalnych nie są zachowywane pomiędzy wywołaniami podprogramów.