MedallionShell icon indicating copy to clipboard operation
MedallionShell copied to clipboard

Killing feature: Kill process and its children

Open rocky05475 opened this issue 7 years ago • 6 comments

This is a little bit hard to Implement but It's a killing feature.

For example: Command.Run("cmd", "/c robocopy..."); Command.KillTree(); // Kill both cmd.exe and robocopy.exe

rocky05475 avatar Jun 23 '17 13:06 rocky05475

This doesn't seem easy to implement on top of .NET APIs; we'd need to either use the TASKKILL windows utility or call into native APIs

madelson avatar Jun 26 '17 11:06 madelson

@rocky05475 I looked into this a bit but I couldn't find a way to implement it that was not Windows or even Windows version-specific (some versions seem to have TSKILL instead of TASKILL).

With the new ProcessId property, it is relatively easy to do a kill tree operation on Windows using TASKILL (which can itself be fired off using a command).

Therefore, I'm going to close this for now. We can reconsider if there is more demand for this feature and/or we can figure out a robust way to do this that works everywhere.

madelson avatar Jul 01 '17 20:07 madelson

Ran into this issue as well. Added an extension as a workaround and thought I would share in case it was useful for anyone else. Can only be used with .NET Core 3.0 which has a new overload for Kill() and should work cross platform. Unfortunately .net standard 2.0 and 2.1 do not yet support this api.

   // DisposeOnExit(false) must be set to false
    public static class CommandExtensions
    {
        public static async Task<Command> WaitAsync(this Command command, TimeSpan timeout, CancellationToken cancellationToken)
        {
            var cancelCompletionSource = new TaskCompletionSource<object>();
            var delayTask = Task.Delay(timeout);
            using (cancellationToken.Register(() => cancelCompletionSource.TrySetCanceled()))
            { 
                await Task.WhenAny(command.Task, delayTask, cancelCompletionSource.Task);
                if (delayTask.IsCompleted || cancelCompletionSource.Task.IsCanceled)
                {
                    command.KillTree();
                }
                return command;
            }
        }

        public static void KillTree(this Command command)
        {
            command.Process.Kill(true);
        }
    }

//usage: await cmd.WaitAsync(TimeSpan.FromSeconds(5), tokenSrc.Token);

adamsd308 avatar Feb 20 '20 18:02 adamsd308

@adamsd308 thanks this is very helpful. With this, I think we can add some APIs that are only on netcoreapp3.0+ to support this.

I think that the cleanest way to represent this in an actual library change would be a new command option:

options => options.OwnsEntireProcessTree(bool value = true)

When this option is set, the following behavioral changes will occur:

  • Kill will call process.Kill(true)
  • options.Timeout(timeout) will call process.Kill(true) if the command times out
  • options.CancellationToken(token) will call process.Kill(true) if the command gets canceled

My thinking is that either you'll be wanting to treat a command as owning its entire tree, or you won't. Either way, you'll want all the mechanisms for killing to be consistent. And, if you want to violate this you can always do so by accessing the underlying process directly through an extension like the one you wrote.

Does that seem reasonable?

madelson avatar Feb 21 '20 11:02 madelson

KillTree() implementation for windows (for possible shimming):

https://github.com/dotnet/runtime/blob/master/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Win32.cs#L382

KillTree() implementation for unix (for possible shimming):

https://github.com/dotnet/runtime/blob/master/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs#L89

madelson avatar Feb 21 '20 11:02 madelson

@madelson adding the additional api for .netcore 3.0+ would be awesome.

adamsd308 avatar Feb 22 '20 08:02 adamsd308