dahall / TaskScheduler

Provides a .NET wrapper for the Windows Task Scheduler. It aggregates the multiple versions, provides an editor and allows for localization.
MIT License
1.2k stars 191 forks source link

Value does not fall within the expected range #935

Closed nvchernov closed 2 years ago

nvchernov commented 2 years ago

Hi, I trying to update some windows tasks, and a I got this exception

Value does not fall within the expected range. at Microsoft.Win32.TaskScheduler.V2Interop.ITaskFolder.RegisterTaskDefinition(String Path, ITaskDefinition pDefinition, Int32 flags, Object UserId, Object password, TaskLogonType LogonType, Object sddl) at Microsoft.Win32.TaskScheduler.TaskFolder.RegisterTaskDefinition(String path, TaskDefinition definition, TaskCreation createType, String userId, String password, TaskLogonType logonType, String sddl) at Microsoft.Win32.TaskScheduler.TaskFolder.RegisterTaskDefinition(String path, TaskDefinition definition) at WinCfg.TaskSchedulerManager.DisableIdleTriggerTasks() in C:\Users\user\source\repos\WinCfg\WinCfg\TaskSchedulerManager.cs:line 29 at WinCfg.Program.<>c.

b__3_1() in C:\Users\user\source\repos\WinCfg\WinCfg\Program.cs:line 71

Code, where I getting exception

public string[] DisableIdleTriggerTasks()
{
    var tasks = TaskService.Instance.AllTasks;

    var idleTriggerTasks = tasks
        .Where(x => x.Definition.Triggers.Any(x => x.TriggerType == TaskTriggerType.Idle))
        .Where(x => x.State != TaskState.Disabled)
        .ToArray();

    foreach (var task in idleTriggerTasks)
    {
        foreach (var t in task.Definition.Triggers.Where(x => x.TriggerType == TaskTriggerType.Idle))
            t.Enabled = false;

        task.Folder.RegisterTaskDefinition(task.Path, task.Definition);// I getting this exception here 
        task.RegisterChanges();

        task.Stop();
        task.Enabled = false;
        task.RegisterChanges();
    }

    return tasks.Select(x => $"* [{x.Name}] [{x.Path}]").ToArray();

}
dahall commented 2 years ago

Currently in your foreach loop, you are re-registering the task with RegisterTaskDefinition and then you are registering it again with RegisterChanges and then stopping the task that may have just started running after registration and then registering it again after disabling, which also registers the task.

Also, instead of x.Definition.Triggers.Any(x => x.TriggerType == TaskTriggerType.Idle) you can use x.Definition.Triggers.ContainsType(typeof(IdleTrigger)). It is a little faster.

Also, to change a trigger, you have to copy it, remove it and re-add it. Yes, that is unintuitive, but it is a design flaw in Microsoft's libraries that I haven't been able to work around.

I would rewrite as:

public string[] DisableIdleTriggerTasks()
{
    var tasks = TaskService.Instance.AllTasks;

    var idleTriggerTasks = tasks
        .Where(x => x.Definition.Triggers.ContainsType(typeof(IdleTrigger)) && x.State != TaskState.Disabled)
        .ToArray();

    foreach (var task in idleTriggerTasks)
    {
        var chg = false;
        for (int i = task.Definition.Triggers.Count - 1; i >= 0; i--)
        {
            var tr = task.Definition.Triggers[i];
            if (tr.TriggerType == TaskTriggerType.Idle)
            {
                task.Definition.Triggers.RemoveAt(i);
                tr.Enabled = false;
                task.Definition.Triggers.Add(tr);
                chg = true;
            }
        }
        if (chg)
            task.RegisterChanges();  // Let me know if this throws an exception
        task.Enabled = false;
    }

    return tasks.Select(x => $"* [{x.Name}] [{x.Path}]").ToArray();
}
nvchernov commented 2 years ago

@dahall it works, thank you very much!