Skip to content

Snapshots and Multi threading

David Hall edited this page Nov 8, 2017 · 1 revision

Snapshots

[This feature is only available with the .NET 4.5.2 build.]

Snapshots allow the developer to capture all the information about the tasks and folders on a given machine and then reapply them to the same machine or another machine. The snapshot is simply a zip file that holds the meta information about tasks and folders (mostly the security applied to them) and then the settings of each task.

Below is an example of how to create a new snapshot and then apply it to a new machine.

const string localSnapshotPath = @"C:\temp\local_snapshot.zip";

// Supplying a cancellation token allows you to interrupt the async methods
var cancelToken = new CancellationTokenSource();

// Create a snapshot from the local machine
// * You must pass in a ConnectionToken pulled from a TaskService instance. This allows the STA based
//   COM object to be created in each thread without exception.
// * The async method will report the path of the item being processed and the percentage completion via a Tuple.
var snapshot = await TaskSchedulerSnapshot.Create(TaskService.Instance.Token, localSnapshotPath,
   cancelToken.Token, new Progress<Tuple<int, string>>(i => ReportProgress(i.Item1, i.Item2)));

// Connect to a remote machine
var remoteTS = new TaskService("RMT-HOST", "user", "DOMAIN", "my$ecretP3d", false);

// Build list of items to restore. You can specify any of the items that are held in the snapshot.
// Alternately, you can provide a list of paths as strings (e.g. "\\AdobeUpdater", @"\Microsoft\Windows\Chkdsk\ProactiveScan").
var items = snapshot.Items.Where(i => !i.Path.Contains("Microsoft"));

// You can choose to apply the security from the snapshot or ignore it
var applySecurity = true;

// You can also choose to overwrite existing tasks and folders or ignore them
var overwrite = false;

// If there are tasks that require passwords you can supply them as a lookup dictionary
var pwds = new Dictionary<string, string>() { { "user", "my$ecretP3d" } };

// Call the Restore method passing in the machine's token. Progress is reported the same as above.
await snapshot.Restore(remoteTS.Token, items, applySecurity, overwrite, pwds, cancelToken.Token,
   new Progress<Tuple<int, string>>(i => ReportProgress(i.Item1, i.Item2)));

Multi-threading

The underlying COM library wrapped by this library is single-threaded. In COM terms, it is an STA object. This means that if you pass a class wrapping one of these COM instances into another thread, you will get COM errors. To get around this, TaskService supports tokens that can be safely passed between threads and then be used to reconstitute the instance. Please note that each new thread will take an initial performance hit as the COM object connects to the target server. This is typically not costly on the local instance, but can take many seconds on remote machines.

This capability is partially demonstrated in the snapshot example above. If you are writing your own multi-threaded app and need to access the Task Scheduler on multiple threads, use the pattern outlined below.

// Create a TaskService instance, local or remote
var ts = new TaskService();

// Note that we're capturing objects that are thread-safe and not objects that wrap native
// COM objects like Task, TaskFolder or TaskDefinition.
var tasks = ts.RootFolder.EnumerateTasks().Select(t => t.Name);

// Capture the token from the TaskService instance
var token = ts.Token;

Parallel.ForEach(tasks, (currentTask) => 
   {
      // Use the token to create a duplicate TaskService instance on this thread
      var thread_ts = TaskService.CreateFromToken(token);
      // Then you are free to use any of the other wrapper classes to perform work
      var xml = thread_ts.RootFolder.Tasks[currentTask].Xml;
      File.WriteAllText($"C:\\temp\\{currentTask}.xml", xml, Encoding.Unicode);
   });