delphi ITaskFolder.RegisterTaskDefinition не работает

Я пытаюсь создать задачу для планировщика задач Windows. Вот код.

procedure TForm1.Button1Click(Sender: TObject);
var
  ts: ITaskService;
  tf: ITaskFolder;
  tf2: ITaskFolder;
  td: ITaskDefinition;
  tr: ITrigger;
  tt: ITimeTrigger;
  at: IAction;
  ae: IExecAction;
  rt: IRegisteredTask;
begin
  CoInitializeEx(nil, COINIT_MULTITHREADED);      
  CoCreateInstance(CLSID_TaskScheduler,nil,CLSCTX_INPROC_SERVER,IID_ITaskService,ts);
  ts.Connect(unassigned, unassigned, unassigned, unassigned);
  try
    tf := ts.GetFolder('\MyFolder');
    tf2 := tf;
  except
    tf := ts.GetFolder('\');
    tf2 := tf.CreateFolder('\MyFolder', unassigned);
  end;
  tf._Release;
  td := ts.NewTask(0);
  td.RegistrationInfo.Author := 'TheAuthor';
  tr := td.Triggers.Create(ttTime);
  tr.QueryInterface(IID_ITimeTrigger, tt);
  tr._Release;
  tt.Id := 'Trigger1';
  tt.StartBoundary := '2017-07-28T01:20:00';
  tt.EndBoundary := '2027-07-28T01:20:00';
  tt._Release;
  at := td.Actions.Create(taExec);
  at.QueryInterface(IID_IExecAction, ae);
  at._Release;
  ae.Path := 'C:\Windows\System32\Notepad.exe';
  ae.WorkingDirectory := 'C:\Windows\System32';
  ae.Arguments := '--help';
  ae._Release;
  rt := nil;
  rt := tf2.RegisterTaskDefinition('MyTestTask', td, 1, unassigned, unassigned, tlInteractiveToken, '');
  rt._Release;
  td._Release;
  tf2._Release;
  CoUninitialize;
end;

Работает нормально до RegisterTaskDefinition метода. Он возвращает ноль, и я думаю, что это ошибка, потому что в моем каталоге в C:\Windows\System32\Tasks не создается задача. Каталог MyFolder создается правильно. Но я даже не могу определить тип ошибки, потому что в версии Delphi TaskScheduler api 2.0 коды ошибок не возвращаются. Вроде бы все параметры функции правильные, но она продолжает возвращать nil вместо объекта IRegisteredTask. Может я что-то забыл, или что-то не так делаю? Я работаю на Windows 7 и Delphi XE 10.2. УАК отключен.


person Alex Pritchin    schedule 27.07.2017    source источник
comment
flags == TASK_VALIDATE_ONLY, Планировщик задач проверяет синтаксис XML, описывающего задачу, но не регистрирует задачу. Еще одна жертва магических значений :)   -  person Victoria    schedule 28.07.2017
comment
Спасибо, это работает. Я неправильно понял, какое значение должно быть в параметре flags. Я думал, что должно быть значение из набора TTaskCreation, где validateonly равно 0, а create равно 1. Но должно быть значение в соответствии с msdn - create равно 2.   -  person Alex Pritchin    schedule 28.07.2017


Ответы (1)


Вы звоните в Метод RegisterTaskDefinition с флагом TASK_VALIDATE_ONLY. Этот флаг описывается так:

TASK_VALIDATE_ONLY

Планировщик заданий проверяет синтаксис XML, описывающего задачу, но не регистрирует задачу. Эту константу нельзя комбинировать со значениями TASK_CREATE, TASK_UPDATE или TASK_CREATE_OR_UPDATE.

Таким образом, вызов возвращает указатель NULL в параметре ppTask, поскольку задача не зарегистрирована. Если бы вы не использовали магические константы и использовали правильно определенные константы, такие как TASK_CREATE вместо 2 (или TASK_VALIDATE_ONLY вместо 1), вы бы обнаружили проблему довольно просто.

person Victoria    schedule 28.07.2017
comment
В библиотеке .pas API 2.0, которую я использую, нет констант TASK_CREATE или TASK_VALIDATE_ONLY. Возможно, у меня неправильная библиотека, но было бы неплохо добавить в нее этот набор констант для дальнейшего использования. В Delphi они вырезали коды ошибок HRESULT (и S_OK) из Result функций и перенесли туда возвращаемые экземпляры интерфейса, такие как IRegisteredTask, которые были переменными параметрами. Поэтому я не вижу, является ли нулевой результат функции ошибкой или нет. Так что проблема у меня была не простая. Но ты прав. Мой код работает без _releases и CoInitializeEx/CoUninitialize. - person Alex Pritchin; 30.07.2017
comment
Я понимаю источник моей проблемы. Существует тип TTaskCreation, который выглядит как TTaskCreation = (tcValidateOnly, tcCreate, tcUpdate = 4, tcCreateOrUpdate = 6, tcDisable = 8, tcDontAddPrincipalAce = $10, tcIgnoreRegistrationTriggers = $20);. Это флаги из msdn, но у этого типа есть одна ошибка, из-за которой функция Ord возвращает неверный результат. Я поставил номер 1 (это действие почти Ord) в вызове функции после того, как получил ошибку - несовместимые типы integer и TTaskCreation. Все, что я должен сделать, чтобы решить эту проблему с нулевым результатом, это исправить TaskCreation TTaskCreation = (tcValidateOnly=1, tcCreate=2, - person Alex Pritchin; 31.07.2017
comment
Теперь я могу использовать его при вызове функции rt := tf2.RegisterTaskDefinition('MyTestTask', td, Ord(tcCreate), unassigned, unassigned, tlInteractiveToken, ''); - person Alex Pritchin; 31.07.2017
comment
Я понимаю. Тогда ваши методы интерфейса используют директиву safecall. Не волнуйтесь, если какой-либо из них не сработает (возвращаемое значение будет отличаться от S_OK), будет возбуждено исключение. Что касается остального, то именно поэтому я всегда перевожу код из Windows SDK (не используя какой-либо импортер или сторонний перевод). - person Victoria; 31.07.2017