1

Currently my code runs a checkout from svn and redirections the stdout and stderr to a textbox using two tasks as seen below. I want to be able to cancel the task immediately when a user clicks the StopButton and be able to cancel the download. I understand that if I changed my cmd.exe command to say something such as "Pause", which continues to run until a user clicks something, I can cancel this command with the StopButton. I am very new to C# and trying to understand why I can cancel that command and not the svn download.

Is this a way I should be implementing my code or should I try a different method?

Thanks so much for the help! I truly appreciate it!

   public Form2()
    {
        InitializeComponent();

    }

    private TaskCompletionSource<bool> _cancelTask;

    private async Task RunProcess()
    {
        Process process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "cmd.exe",
                Arguments = "svn download"
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                CreateNoWindow = false,
            }
        };


        Console.Write(URL);
        process.Start();

        Task readerTasks = Task.WhenAll(
            ConsumeReader(process.StandardError),
            ConsumeReader(process.StandardOutput));

        Task completedTask = await Task.WhenAny(readerTasks, _cancelTask.Task);

        if(completedTask == _cancelTask.Task)
        {
            process.Kill();
            await readerTasks;
            throw new TaskCanceledException(_cancelTask.Task);
        }
    }

    private async Task ConsumeReader(TextReader reader)
    {
        char[] text = new char[512];
        int cch;

        while ((cch = await reader.ReadAsync(text, 0, text.Length)) > 0)
        {
            textBox2.AppendText(new string(text, 0, cch));
        }
    }

    private async void Submit_Click(object sender, EventArgs e)
    {

        Submit.Enabled = false;
        StopButton.Enabled = true;
        _cancelTask = new TaskCompletionSource<bool>();

        try
        {
            await RunProcess(cts.Token);
        }
        catch (OperationCanceledException)
        {
            MessageBox.Show("The operation was cancelled");
        }
        finally
        {
            _cancelTask = null;
            Submit.Enabled = true;
            StopButton.Enabled = false;
        }
    }

    private void StopButton_Click(object sender, EventArgs e)
    {
        _cancelTask.SetCanceled();
    }
Ross Rhone
  • 197
  • 3
  • 14
  • You're sending a kill signal to `cmd`, not `svn` or whatever other child processes there are. If you want a more "structured" approach to killing trees of external processes, check out the [Job API](https://msdn.microsoft.com/en-us/library/windows/desktop/ms684161(v=vs.85).aspx). You'll have to do your own p/Invokes to use it from .NET, though. – Stephen Cleary Aug 11 '16 at 20:46
  • In addition to the previous comment, note that unless you specifically want to interact with the command-line interpreter (cmd.exe), you can just run `svn` directly, with the `download` passed as the argument to that task. The above question, to be answered properly, will require someone who actually _knows_ what the `svn` utility does, process-wise, and what parts of it are still running after you kill the cmd.exe process that had started it. But I would first try just running `svn` directly, and see if killing that suffices. – Peter Duniho Aug 11 '16 at 20:50
  • Stephen Cleary and Peter Duniho you are completely right. If I changed the file name to "svn.exe" it will terminate that one task. Thank you both very much for your time and effort on this question! – Ross Rhone Aug 11 '16 at 20:53

1 Answers1

0

I found a solution, not sure if this is the best, but you can start another process to start the command line command taskkill to kill all of the .exe's that are running. I now kill the cmd.exe (parent) and the svn.exe (child) that was called from the cmd.exe!

This is only useful if you know the .exes that are started from the parent if there were many more children created it would be best to find a different solution to recursively go and find the children processes.

    if(completedTask == _cancelTask.Task)
    {
        //process.Kill(); This would only kill the CMD.exe
        Process exit = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = "cmd.exe",
                    //Arguments = "/K Pause",
                    Arguments = "/C taskkill /f /im svn.exe & taskkill /f /im cmd.exe",
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    CreateNoWindow = false,
                }
            };
            exit.Start();
        await readerTasks;
        throw new TaskCanceledException(_cancelTask.Task);
    }
Ross Rhone
  • 197
  • 3
  • 14