Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Планировщик заданий


Автор: Snowy 28.8.2006, 17:32
Написал класс, управляющий встроенным Планировщиком Windows.
Просьба протестировать.
Возможности:
- добавление заданий
- удаление заданий
- список существующих заданий
- список периодов срабатывания задания
- добавление периодов срабатывания
- изменение параметров "путь, комментарий и т.д."
В аттаче модули и пример.

Не смог победить проблему с повторным запуском.
Точнее вроде работает, но максимальный период 9999 минут.
Может у кого будут какие идеи, как можно правильно настроить повтор.
Без указания конечной даты будет запускаться ежедневно в указанное время.
Также есть хитрый флаг TriggerType. Он работает по умолчанию, но можно сменить его на:
TASK_EVENT_TRIGGER_AT_SYSTEMSTART - запускать таск при старте системы
TASK_EVENT_TRIGGER_AT_LOGON - запускать при логоне

Добавлено @ 17:34 
ЗЫ. Еженедельный, ежемесячный и ежегодный запуск не реализовывал - ИМХО не нужны они для программного управления.

Автор: drkot 28.8.2006, 17:41
Неплохо. 
Цитата(Snowy @  28.8.2006,  17:32 Найти цитируемый пост)
Не смог победить проблему с повторным запуском.Точнее вроде работает, но максимальный период 9999 минут.

можно вместо минут поставить часы.
больше 9999 задать нельзя, там проверка стоит и рубит лишнее.

ЗЫЖ я всетаки к сторонним планировщикам склоняюсь (у встроеного зачастую гибкости нехватает).

Автор: Snowy 28.8.2006, 17:49
Не спорю. Зато стандартный гарантировано уже есть у пользователя.
Я знаю, что можно задавать в часах. Но эта сволочь никак не хочет на них реагировать.
Я и минуты методом тыка хрен знает за сколько заставил работать.
8 раз этот раздел MSDN'a перечитал. Там всё слишком кратко. А начинаешь делать - не слушается smile
Но более-менее нормальный функционал получился.

Автор: Elfebet 28.8.2006, 17:59
Я так думаю задавать периодичность по минутам - это не правильно. Стандартном планировщике периодичность задается в: днях, неделях, месяцах и однократно. Т.е. нужно как-то явно указывать планировщику что выполнение будет каждые 3 дня и т.п. так что здесь эти 4 вида периодичности играют важную роль.

Автор: drkot 28.8.2006, 17:59
Цитата(Snowy @  28.8.2006,  17:49 Найти цитируемый пост)
Там всё слишком кратко

как впрочем и весь МСДН.  smile 
Написано красиво, а работать в лучшем случае в 2 ночи начинает кошда по три спички в глазах.  smile 
Цитата(Snowy @  28.8.2006,  17:49 Найти цитируемый пост)
Зато стандартный гарантировано уже есть у пользователя.

Согласен. для внедрения в свое ПО вещь "золотая". (после установки системы ету службу сразу отключаю тк практически не пользуюсь)

Добавлено @ 18:01 
Цитата(Elfebet @  28.8.2006,  17:59 Найти цитируемый пост)
Я так думаю задавать периодичность по минутам - это не правильно. 

не ты неправильно понял, есть возможность задать произвольный интервал повлорения (например каждые 5 минут)

Добавлено @ 18:03 
Цитата(Snowy @  28.8.2006,  17:49 Найти цитируемый пост)
Я знаю, что можно задавать в часах. Но эта сволочь никак не хочет на них реагировать.

посмотрю может чего накопаю.

Автор: Elfebet 28.8.2006, 18:18
вот что я нашел в файле mstask.pas:
Код

type
  PTaskTriggerType = ^TTaskTriggerType;
  _TASK_TRIGGER_TYPE = (
    TASK_TIME_TRIGGER_ONCE,
    TASK_TIME_TRIGGER_DAILY,
    TASK_TIME_TRIGGER_WEEKLY,
    TASK_TIME_TRIGGER_MONTHLYDATE,
    TASK_TIME_TRIGGER_MONTHLYDOW,
    TASK_EVENT_TRIGGER_ON_IDLE,
    TASK_EVENT_TRIGGER_AT_SYSTEMSTART,
    TASK_EVENT_TRIGGER_AT_LOGON,
    TASK_TRIGGER_TYPE);

наверное соответсвенно и выставляется число от 0 - 9999. Т.е. наверное если:
TASK_TIME_TRIGGER_DAILY, то от 1 до 31
TASK_TIME_TRIGGER_WEEKLY, то от 1 до 52
тока вот не понял TASK_TIME_TRIGGER_MONTHLYDATE и  TASK_TIME_TRIGGER_MONTHLYDOW....
сто пудова от 0-9999 - может быть не только минуты...

Автор: Snowy 28.8.2006, 19:46
Ну там далеко не так всё просто.
Я попозже сделаю перегруженные функции для добавления других вариантом.
Сейчас вариантов два: Задавать период в минутах, либо ежедневный запуск.
Но можно сделать настройку интервала в днях. Можно и по дням недели. Можно и по дням месяца.
Год, пожалуй делать уже не буду. Хотя в принципе можно. Посмотрим.
С периодом проблем нет. Проблемы с повторением.

Цитата(Elfebet @  28.8.2006,  17:59 Найти цитируемый пост)
Стандартном планировщике периодичность задается в: днях, неделях, месяцах и однократно.
Угу. Это всё ясно. Просто у однократного запуска есть ещё такой параметр - повторять N-ый период.

Автор: Elfebet 29.8.2006, 13:55
Snowy, прошу помоги разобраться как это выставлять период. что у меня ничего не получается. (ну к примеру выставить Ежемесячно, с расписанием по месяцам и выбор месяцов). ну прям горит smile 

Автор: Snowy 29.8.2006, 14:06
Пока никак. Нужно доработать такие возможности.
Если хочешь сам попробовать, то дорога тебе в MSDN.
Но не думаю, что разберёшься - тап придумано, как обычно через заднюю ногу.

Автор: Elfebet 30.8.2006, 09:19
Цитата

Но не думаю, что разберёшься - тап придумано, как обычно через заднюю ногу. 

Все таки что хотел узнать - узнал и разобрался. Вот я сделал маленькую програмку подобно планировщику задач (но не все возможности реализованы).
Вот основной код создания задания
Код

procedure TForm1.Button1Click(Sender: TObject);
var WorkItem, PITask: IUnknown;
    piNewTrigger, tWord: Word;
    ITTrigger: ITaskTrigger;
    TaskTrig: TTaskTRIGGER;
    uname, pass: PWideChar;
begin
tWord:=0;
if Trim(Edit3.Text)='' then begin ShowMessage('Не задано "Имя задания"'); Exit; end;
if Trim(Edit1.Text)='' then begin ShowMessage('Не задано "Запуск прораммы"'); Exit; end;
  // проверяем если задание с таким именем существует тогда удаляем
  Task := nil;
  if SchedulingAgent.Activate(StrToWide(Edit3.Text), IID_ITask, PITask) = S_OK then Task := ITask(PITask);
  if Task <> nil then SchedulingAgent.Delete(StrToWide(Edit3.Text));
  // заносим имя задания
  SchedulingAgent.NewWorkItem(StrToWide(Edit3.Text), CLSID_CTask, IID_IScheduledWorkItem, WorkItem);
  Task := ITask(WorkItem);
  ScheduledWorkItem := IScheduledWorkItem(WorkItem);
  // запуск программы
  Task.SetApplicationName(StrToWide(Edit1.Text));
  // параметры запуска программы
  Task.SetParameters(StrToWide(Edit2.Text));
  // рабочая директория
  Task.SetWorkingDirectory(StrToWide(ExtractFileDir(Edit1.Text)));
  // комментарии
  Task.SetComment(StrToWide(mComment.Text));
  // устанавливаем акаунт юзера (имя, пароль пользователя)
  GetMem(uname, 255);
  GetMem(pass, 255);
  StringToWideChar(edName.Text, uname, Length(edName.Text) + 1);
  StringToWideChar(edPass.Text, pass, Length(edPass.Text) + 1);
  Task.SetAccountInformation(uname, pass);
  FreeMem(uname);
  FreeMem(pass);
  // устанавливаем с какой даты выполнять задание
  Task.CreateTrigger(piNewTrigger, ITTrigger);
  ZeroMemory(@TaskTrig, sizeof(TASK_TRIGGER));
  TaskTrig.cbTriggerSize := sizeof(TASK_TRIGGER);
  TaskTrig.wBeginYear := YearOf(dtDate.Date);
  TaskTrig.wBeginMonth := MonthOf(dtDate.Date);
  TaskTrig.wBeginDay := DayOf(dtDate.Date);
  TaskTrig.wEndYear := 0;
  TaskTrig.wEndMonth := 0;
  TaskTrig.wEndDay := 0;
  // время запуска
  TaskTrig.wStartHour := HourOf(dtTime.Time);
  TaskTrig.wStartMinute := MinuteOf(dtTime.Time);
  // тип запуска
  case ComboBox1.ItemIndex of
    0: begin // если ежедневно
         TaskTrig.TriggerType:=TASK_TIME_TRIGGER_DAILY;
         TaskTrig.Type_.Daily.DaysInterval:=StrToInt(SpinEdit1.Text);
       end;
    1: begin // если еженедельно
         TaskTrig.TriggerType:=TASK_TIME_TRIGGER_WEEKLY;
         TaskTrig.Type_.Weekly.WeeksInterval:=StrToInt(SpinEdit2.Text);
         if chkW1.Checked then tWord:=tWord+TASK_MONDAY;
         if chkW2.Checked then tWord:=tWord+TASK_TUESDAY;
         if chkW3.Checked then tWord:=tWord+TASK_WEDNESDAY;
         if chkW4.Checked then tWord:=tWord+TASK_THURSDAY;
         if chkW5.Checked then tWord:=tWord+TASK_FRIDAY;
         if chkW6.Checked then tWord:=tWord+TASK_SATURDAY;
         if chkW7.Checked then tWord:=tWord+TASK_SUNDAY;
         TaskTrig.Type_.Weekly.rgfDaysOfTheWeek:=tWord;
       end;
    2: begin // если ежемесячно
         if chkMonth1.Checked then tWord:=tWord+TASK_JANUARY;
         if chkMonth2.Checked then tWord:=tWord+TASK_FEBRUARY;
         if chkMonth3.Checked then tWord:=tWord+TASK_MARCH;
         if chkMonth4.Checked then tWord:=tWord+TASK_APRIL;
         if chkMonth5.Checked then tWord:=tWord+TASK_MAY;
         if chkMonth6.Checked then tWord:=tWord+TASK_JUNE;
         if chkMonth7.Checked then tWord:=tWord+TASK_JULY;
         if chkMonth8.Checked then tWord:=tWord+TASK_AUGUST;
         if chkMonth9.Checked then tWord:=tWord+TASK_SEPTEMBER;
         if chkMonth10.Checked then tWord:=tWord+TASK_OCTOBER;
         if chkMonth11.Checked then tWord:=tWord+TASK_NOVEMBER;
         if chkMonth12.Checked then tWord:=tWord+TASK_DECEMBER;
         if RadioButton1.Checked then begin
           TaskTrig.TriggerType:=TASK_TIME_TRIGGER_MONTHLYDATE;
           TaskTrig.Type_.MonthlyDate.rgfDays:=humanDaysToBitField(StrToInt(SpinEdit3.Text));
           TaskTrig.Type_.MonthlyDate.rgfMonths:=tWord;
         end else begin
           TaskTrig.TriggerType:=TASK_TIME_TRIGGER_MONTHLYDOW;
           case ComboBox2.ItemIndex of
             0: TaskTrig.Type_.MonthlyDOW.wWhichWeek:=TASK_FIRST_WEEK;
             1: TaskTrig.Type_.MonthlyDOW.wWhichWeek:=TASK_SECOND_WEEK;
             2: TaskTrig.Type_.MonthlyDOW.wWhichWeek:=TASK_THIRD_WEEK;
             3: TaskTrig.Type_.MonthlyDOW.wWhichWeek:=TASK_FOURTH_WEEK;
             5: TaskTrig.Type_.MonthlyDOW.wWhichWeek:=TASK_LAST_WEEK;
           end;
           case ComboBox3.ItemIndex of
             0: TaskTrig.Type_.MonthlyDOW.rgfDaysOfTheWeek:=TASK_MONDAY;
             1: TaskTrig.Type_.MonthlyDOW.rgfDaysOfTheWeek:=TASK_TUESDAY;
             2: TaskTrig.Type_.MonthlyDOW.rgfDaysOfTheWeek:=TASK_WEDNESDAY;
             3: TaskTrig.Type_.MonthlyDOW.rgfDaysOfTheWeek:=TASK_THURSDAY;
             4: TaskTrig.Type_.MonthlyDOW.rgfDaysOfTheWeek:=TASK_FRIDAY;
             5: TaskTrig.Type_.MonthlyDOW.rgfDaysOfTheWeek:=TASK_SATURDAY;
             6: TaskTrig.Type_.MonthlyDOW.rgfDaysOfTheWeek:=TASK_SUNDAY;
           end;
           TaskTrig.Type_.MonthlyDOW.rgfMonths:=tWord;
         end;
       end;
     // иначе однократно
     else TaskTrig.TriggerType:=TASK_TIME_TRIGGER_ONCE;
  end;
  ITTrigger.SetTrigger(@TaskTrig);
  // сохраняем
  Task.QueryInterface(IID_IPersistFile, pIPersistFile);
  if pIPersistFile <> nil then pIPersistFile.Save(nil, true);
  // показываем список заданий
  Button3Click(self);
end;


И еще планировщик задания будет запускать задания если он конечно не отключен и нужно ставить обязательно имя и пароль пользователя, елси пароль пустой то задание не запуститься.

Snowy, спасибо за помощь!

Прошу оценить. smile 

Автор: Snowy 30.8.2006, 10:14
Ну надо же. И даже работает smile
Доработаю свой класс, если кому нужно будет.
Пока не нужно - не буду на это время тратить.

Автор: drkot 30.8.2006, 15:25
Правилно. Главное принципы понять, чтоб когда понадобится парева меньше было.

Автор: Snowy 30.8.2006, 16:12
ХМ. Только щаз заметил, что у тебя расписание задаётся в момент создания.
А как же несколько расписаний?
Если так уж нужен ежемесячный запуск, то можно и моим классом создать 12 расписаний - по одному на каждый месяц smile
Хотя конечно по честному нужно делать полнофункционал...
Но лень smile
Полноценное управление и так есть - через стандартную управлялку шедулером.
А данная фень нужна только для автоматического создания задания. 
Например запускать себя автоматом по ночам.

Автор: Elfebet 30.8.2006, 16:20
Snowy, совершенно верно! Лень. И я не ставил задачу сделать несколько расписаний.

Добавлено @ 16:21 
ну если вдруг кому-то понадобится несколько расписаний, могу и доработать...

Автор: SamStone 3.10.2006, 22:04
Апну тему в надежде на ответ (и чего я ее на месяц раньше не нашел? smile )
У планировщика можно выставить крыжик чтобы уведомлять юзверя о пропущеных заданиях (комп был выключен и прочее). Можно ли вытащить пропущенные времена запуска или самому придется высчитывать?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)