Reputation: 2763
I am creating an application that creates a scheduled task for every user on first logon. I am using NuGet package Task Scheduler Managed Wrapper 2.5.21. When the exe run on logon, the Access Denied error occurs. When manually run the exe as Administrator, the Scheduled Task is created. How can I overcome this issue?
string installPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
using (TaskService ts = new TaskService())
{
TaskDefinition td = ts.NewTask();
td.Actions.Add(new ExecAction("MyExe.exe", null, installPath));
td.Triggers.Add(new SessionStateChangeTrigger
{
StateChange = TaskSessionStateChangeType.SessionUnlock,
UserId = Environment.UserName
});
td.Principal.RunLevel = TaskRunLevel.Highest;
td.Principal.LogonType = TaskLogonType.InteractiveToken;
ts.RootFolder.RegisterTaskDefinition("task_" + Environment.UserName, td);
}
Upvotes: 2
Views: 4781
Reputation: 1629
For non-administrative or non-elevated user every trigger type have a set of restrictions. E.g. you can not add OnLogon trigger for somebody else, only for yourself. Looks like you also cannot add a SessionStateChangeTrigger
at all.
This is working example based on author's answer to my issue: https://github.com/dahall/TaskScheduler/issues/58
var td = ts.NewTask();
td.Actions.Add("notepad", null, null);
td.Triggers.Add(new LogonTrigger { UserId = WindowsIdentity.GetCurrent().Name });
ts.RootFolder.RegisterTaskDefinition("Test", td);
Note: if you are creating task with TaskRunLevel.Highest
for administrator under UAC this is only possible after elevation (otherwise that would be a huge vulnerability that makes UAC useless)
Note: if you are creating task for each user that launches application - you have to include User SID in a task name. This is guaranteed to be unique even if users will be renamed. Otherwise there would be access denied because same task already created by other user (and he have more rights on it than you). Consider DropBox, OneDrive, GoogleDrive - thery are all adding SID to the task name.
This is how I did it in the end (including different task name for Highest
version and normal one:
private static void CreateTask(string path, string taskFolder, bool highest)
{
var sid = WindowsIdentity.GetCurrent().User.Value;
var userId = WindowsIdentity.GetCurrent().Name;
using (var ts = new TaskService())
{
var td = ts.NewTask();
if (highest)
{
td.Principal.RunLevel = TaskRunLevel.Highest;
}
td.Actions.Add(path, "/a", null);
td.Triggers.Add(new LogonTrigger { UserId = userId, });
ts.RootFolder.RegisterTaskDefinition(taskFolder + "MyApp Task-" + sid + (highest ? "h" : ""), td);
}
}
Upvotes: 5