This project has moved. For the latest updates, please go here.

Unauthorized Exception when creating/deleting tasks

Jul 16, 2012 at 7:00 AM

Hi,

I'm getting errors when trying to create or delete a tasks for the current user. I have not been able to repo this on my machine at all. However, I can repo this on any test machine (i.e. no VS installed and user is not running with admin permissions).

System.UnauthorizedAccessException: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))   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)

Here's my code:

using (var ts = new TaskService())
{
var taskDef = ts.NewTask();
taskDef.RegistrationInfo.Description = string.Format(
                            Resources.Scheduled_Task_Desc_Format,
                            this.TargetEntity.FriendlyName
                        );
taskDef.Triggers.Add(GetTrigger(_interval));

taskDef.Actions.Add(
                 new ExecAction(
                     Assembly.GetEntryAssembly().Location,
                     "-s " + GetSyncFlowName(this.TargetEntity)
                 )
             );
ts.RootFolder.RegisterTaskDefinition(taskName, taskDef);
}

for deletes

var taskFilter = new Regex(
                     string.Concat("^", taskName, "$"),
                     RegexOptions.Singleline | RegexOptions.Compiled
                 );
// Find the existing instance of the task..
var olTask = ts.RootFolder.GetTasks(taskFilter).FirstOrDefault();
if (olTask != null)
{
	ts.RootFolder.DeleteTask(taskName);
}
 

Coordinator
Jul 16, 2012 at 5:37 PM

Check these two links for information about rights required on systems to register and delete tasks:

Jul 17, 2012 at 7:17 AM

Thanks Dahall. 
Does this mean that a application opened by a standard user would not be able to even schedule a taks that does not require elevated permissions?

I've verified that the standard user can schedule and delete tasks by going to Scheduled Tasks in Control Panel. The task needs to execute only for the current user and in the current user's context.

Coordinator
Jul 17, 2012 at 10:40 PM

I haven't tried your specific scenario, but the Scheduled Tasks applet uses the same base COM library as this library. I would expect that you will need to specify the username and logontype of S4U or InteractiveToken in the RegisterTaskDefinition method and ensure that your settings in code match the settings of those tasks created using the applet.

Jul 18, 2012 at 7:50 AM
Edited Jul 18, 2012 at 11:35 AM

This is the latest code I tried. I'm pretty sure I'm just missing something small. Your help is greatly appreciated:

 


 

 

var taskDef = ts.NewTask();

if (ts.HighestSupportedVersion >= new Version(1, 2))
{
	taskDef.Principal.RunLevel = TaskRunLevel.LUA;
}
else
{
	taskDef.Settings.RunOnlyIfLoggedOn = true;
}

string userId = string.Concat(Environment.UserDomainName, "\\", Environment.UserName);

taskDef.Principal.LogonType = TaskLogonType.S4U;
taskDef.Principal.UserId = userId;

taskDef.RegistrationInfo.Description = string.Format(
							Resources.Scheduled_Task_Desc_Format,
							this.TargetEntity.FriendlyName
						);
taskDef.Triggers.Add(GetTrigger(_interval));
taskDef.Actions.Add(
				new ExecAction(
					Assembly.GetEntryAssembly().Location,
					"-s " + GetSyncFlowName(this.TargetEntity)
				)
			);


ts.RootFolder.RegisterTaskDefinition(
				taskName, 
				taskDef,
				TaskCreation.CreateOrUpdate,
				userId,
				LogonType: TaskLogonType.S4U
			);
Jul 18, 2012 at 9:37 AM

Great news! I found the solution based on the following thread: 

http://social.technet.microsoft.com/Forums/is/winserverGP/thread/6eb2bbb4-4444-47f4-b4f1-dbac43f33561

Based on the two recommendations, 

  1. Add a Execution Time limit
  2. Created the task on a sub folder rather than the root folder

I'm not sure how critical #1 is, however #2 will get you past this exception. Creates and Deletes all work now :)

Jul 18, 2012 at 12:53 PM
Edited Jul 18, 2012 at 1:26 PM

For everyone's benefit, here is the simplified code to make this work for non-admin users on XP and Win7

I think I'll be sleeping better from today since this is resolved :D


var taskDef = ts.NewTask();

taskDef.Settings.ExecutionTimeLimit = TimeSpan.FromMinutes(15);
taskDef.RegistrationInfo.Description = "My Task";

taskDef.Actions.Add(new ExecAction(
					Assembly.GetEntryAssembly().Location,
					"-myArgs"
				)
			);

string userId = string.Concat(Environment.UserDomainName, "\\", Environment.UserName);
TaskLogonType logonType;
if (ts.HighestSupportedVersion >= _v2)
{
	taskDef.Principal.RunLevel  = TaskRunLevel.LUA;
	taskDef.Principal.LogonType = logonType = TaskLogonType.S4U;
	taskDef.Principal.UserId    = userId;
	taskDef.Triggers.Add(GetTrigger(interval));
}
else // For Windows XP
{
	taskDef.Settings.RunOnlyIfLoggedOn = true;
	logonType = TaskLogonType.InteractiveToken;

	// Avoid System.ArgumentException: 
	//       Trigger.Repetition.Interval must be less than 
	//       Trigger.Repetition.Duration under Task Scheduler 1.0
	var trigger   = GetTrigger(interval);
	trigger.Repetition.Duration = trigger.Repetition.Interval.Add(TimeSpan.FromMinutes(1));
	trigger.Repetition.StopAtDurationEnd = false;
	taskDef.Triggers.Add(trigger);
}

var taskFolder = GetTaskFolder(ts);
taskFolder.RegisterTaskDefinition(
				taskName,
				taskDef,
				TaskCreation.CreateOrUpdate,
				userId,
				LogonType: logonType
			);
		
// Add this helper method		
private TaskFolder GetCeligoTaskFolder(TaskService ts)
{
	if (ts.HighestSupportedVersion >= _v2)
	{
		return ts.RootFolder
				 .SubFolders
				 .FirstOrDefault(f => f.Name == "MyTaskFolder")
				 ?? ts.RootFolder.CreateFolder("MyTaskFolder");
	}
	else
	{
		return ts.RootFolder;
	}
}

Marked as answer by dahall on 10/24/2014 at 7:28 AM