Closed GromitD90 closed 5 years ago
When creating a task with the "Run only when user is logged on" option, you are effectively creating a task to run as a specific user using the TaskLogonType.InteractiveToken registration option. The export therefore includes the SID of the user so that specific user would have the interactive right when imported to a different machine. Here's the XML segment, which format is defined by Microsoft:
<Principals>
<Principal id="Author">
<UserId>S-1-5-21-123456789-1234567898-123456789-123456</UserId>
<LogonType>InteractiveToken</LogonType>
<RunLevel>LeastPrivilege</RunLevel>
</Principal>
</Principals>
In your export, you'll see your account's SID under the UserId
tag. When importing, you will get an error if this SID is not recognized on the importing machine.
As an aside, you do not need to create a TaskService
instance if you are using static Instance
. Your code should look like:
string taskname = Path.GetFileNameWithoutExtension(item);
try { TaskService.Instance.RootFolder.ImportTask(taskname, item); }
catch (Exception e)
{
Console.WriteLine("Exception when adding to task scheduler", e);
Console.WriteLine("Make sure you are running App in Administrator mode");
}
If this is not the problem, please include the full exception information in your reply so I can better diagnose the issue.
Also, given that this library simply wraps the Microsoft API for the Windows Task Scheduler, you don't need to write your own import program. Every Windows machine since Vista includes the SCHTASKS.EXE command-line tool which can do this import.
SCHTASKS /Create /XML "filename.xml" [/RU username]
Thanks Dave. I’ve tried using the schtasks command line approach but I cannot get it to work without supplying a username and password of the target system – for example this works
schtasks /Create /tn TaskName /XML "Path to xml file” /RU username /RP password
If I omit the /RU and /RP entries it gives me an error “No mapping between account names and security ids was done”
If I run Import Task from the Task Scheduler I do not have to supply any username and password – it just creates a task that uses the account that was currently active when the Import was done and “Run only when user is logged on” is selected, which is what I want.
Is there any way on the command line to force a “use currently active account credentials” ?
Mike
I think I understand what you're asking. Instead of using ImportTask
, use the RegisterTask
method and pull in the XML file using System.IO.File.ReadAllText(xmlFileName)
. This let's you have full control over the account used to run the task and how that account is configured. I think for what you want, you could use:
string taskname = Path.GetFileNameWithoutExtension(item);
try { TaskService.Instance.RootFolder.RegisterTask(taskname, File.ReadAllText(xmlFileName),
TaskCreation.CreateOrUpdate, null /* Current User */, null /* No Pwd */, TaskLogonType.InteractiveToken); }
catch (Exception e)
{
Console.WriteLine("Exception when adding to task scheduler", e);
Console.WriteLine("Make sure you are running App in Administrator mode");
}
I tried this but I still get an exception when trying to create the task scheduler entry. For the life of me I cannot get the Exception e to display
I’ve tried e.ToString(); in the console.writeline and also e.GetBaseException().ToString() but nothing gets written to the console
I got the following details from event viewer
Application: ConsoleApp15.exe Framework Version: v4.0.30319 Description: The process was terminated due to an unhandled exception. Exception Info: System.Runtime.InteropServices.COMException at Microsoft.Win32.TaskScheduler.V2Interop.ITaskFolder.RegisterTask(System.String, System.String, Int32, System.Object, System.Object, Microsoft.Win32.TaskScheduler.TaskLogonType, System.Object) at Microsoft.Win32.TaskScheduler.TaskFolder.RegisterTask(System.String, System.String, Microsoft.Win32.TaskScheduler.TaskCreation, System.String, System.String, Microsoft.Win32.TaskScheduler.TaskLogonType, System.String) at ConsoleApp15.Program.Main(System.String[])
Here is the code as it currently stands. The first Console.writeline displays the correct path to the xml file
foreach (string item in files) { Console.WriteLine(item); // each item is the full pathname to the xml file string taskname = Path.GetFileNameWithoutExtension(item); try { TaskService.Instance.RootFolder.RegisterTask(taskname, File.ReadAllText(item), TaskCreation.CreateOrUpdate, null / Current User /, null / No Pwd /, TaskLogonType.InteractiveToken); } catch (Exception e) { Console.WriteLine("Exception when adding to task scheduler", e.GetBaseException().ToString()); Console.WriteLine("Make sure you are running App in Administrator mode"); }
I just ran the following successfully. Hopefully it will help with another way to do this:
const string tPath = @"C:\Users\User1\Desktop\Test.xml";
Task t = ts.AddTask("Test", QuickTriggerType.Daily, "myprogram.exe", null, null, null, TaskLogonType.InteractiveToken, null);
t.Export(tPath);
ts.RootFolder.DeleteTask("Test", false);
TaskDefinition td = ts.NewTask();
td.XmlText = System.IO.File.ReadAllText(tPath);
// I did this with SYSTEM, but you could put any user options here,
// including the one I showed earlier.
ts.RootFolder.RegisterTaskDefinition("Test", td, TaskCreation.Create, "SYSTEM", null, TaskLogonType.ServiceAccount, null);
Sorry for the delay in getting back to you. I tried the code you sent to me and yes I can import the xml file however it always ends up with the option Run whether the user is logged on or not.
I modified the code to stop after your initial task creation and export. I looked at the task “test” in the task manager and it clearly shows Run only when the user is logged on.
After the code deletes the task and reimports it the setting is now Run whether the user is logged on or not.
If I manually import the exported xml file it is created correctly with Run only when the user is logged in.
I didn’t try playing with a user account vs the system setting.
If you want to have it be "Run whether the user is logged on or not" and NOT have a password, then you must set the user name to the current user (or null
) and TaskLogonType.S4U
when you register the task. If you want "Run only when the user is logged on" use TaskLogonType.InteractiveToken
.
// Run whether the user is logged on or not
ts.RootFolder.RegisterTaskDefinition("Test", td, TaskCreation.Create, "DOMAIN\\username", null, TaskLogonType.S4U, null);
// Run only when the user is logged on
ts.RootFolder.RegisterTaskDefinition("Test", td, TaskCreation.Create, null, null, TaskLogonType.InteractiveToken, null);
Again many thanks for your help. It’s all working correctly for me now.
I can’t say how much I appreciate your patience.
When using TaskService ImportTask to import an xml file which had the Security setting "Run only when user is logged on" selected the resultant imported Task has the security setting "Run whether user is logged on or not" selected. Importing the xml file manually works correctly.
Note that I have only been able to test this where the xml file was exported and imported (using a Program calling ImportTask) on the same PC. Trying to run the same Program on another PC results in an exception (which doesn't appear to return any details) and the task is not Imported.
Here is a snippet of the test code I'm using
string taskname = Path.GetFileNameWithoutExtension(item); using (TaskService ts = new TaskService()) { try { TaskService.Instance.RootFolder.ImportTask(taskname, item); } catch (Exception e) { Console.WriteLine("Exception when adding to task scheduler", e); Console.WriteLine("Make sure you are running App in Administrator mode"); }
The first Console.WriteLine does not print anything for e
Steps to reproduce the behavior:
Expected behavior
Environment (please complete the following information):