I'm working on an internal application to control operations on PDF documents using a command line utility. I'm trying to build a CommandManager class to allow multiple operations to run at once, allow them to be cancelled, etc.
I have a wrapper object (PdfCommandTask) that I use to manage the command(s) I want to execute, cancellation source, and task. I then keep a list of these to manage in my UI. ProcessAsync is a Task-based solution for running a Process object that I found here:
ProcessStartInfo hanging on "WaitForExit"? Why?
I'm fairly noobish to the TPL and I've run into a problem where my continuation in the following code doesn't fire when I cancel the task... it does fire on normal task completion or a different error condition.
In the debugger, it follows the Exception catch path, sets my result object, and hits the return statement of the first task... but then proceeds on without entering the continuation and moves out of the method.
I've been through several iterations and read numerous posts, but I've yet to figure out my issue. Any thoughts would be greatly appreciated.
Note: I realize the AggregateException / Exception handling is probably redundant, I was just trying to cover my bases in my testing.
Thanks.
public void Execute(PdfCommand command)
{
PdfCommandResult result = command.Valid();
if (!result.Success)
{
CommandCompleted?.Invoke(this, result);
return;
}
command.BuildCommands();
var cmdTask = new PdfCommandTask(command);
var token = cmdTask.CancelTokenSource.Token;
// Add the task to the task list
CommandList.Add(cmdTask);
cmdTask.Task = Task.Run<PdfCommandResult>(async () =>
{
Debug.Print("Spinning up task...");
var pdfResult = new PdfCommandResult();
List<Task> taskList = new List<Task>();
foreach (var cmd in command.Commands)
{
var procTask = ProcessAsync.StartProcess(Pdftk, cmd, cmdTask.CancelTokenSource, PdftkPath);
taskList.Add(procTask);
}
try
{
if (token.IsCancellationRequested)
token.ThrowIfCancellationRequested();
await Task.WhenAll(taskList);
}
catch (AggregateException e)
{
Debug.Print("AggregateException thrown.");
if (e.InnerException is OperationCanceledException)
pdfResult = new PdfCommandResult("The task was cancelled.");
else
pdfResult = new PdfCommandResult("The task faulted... Error: " + e.InnerException);
}
catch (Exception e)
{
Debug.Print("Exception thrown.");
if (e is OperationCanceledException)
pdfResult = new PdfCommandResult("The task was cancelled.");
else
pdfResult = new PdfCommandResult("The task faulted... Error: " + e);
}
return pdfResult;
}, token).ContinueWith((prevTask) =>
{
Debug.Print("ContinueWith fired.");
// Get the previous task result
result = prevTask.Result;
// Remove the task from the task list
CommandList.Remove(cmdTask);
// Let anyone listening know that the command completed (it may have been cancelled or faulted)
CommandCompleted?.Invoke(this, result);
}, token, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}