Kill process tree programmatically in C#
Asked Answered
L

11

64

I am starting Internet Explorer programmatically with code that looks like this:

ProcessStartInfo startInfo = new ProcessStartInfo("iexplore.exe");
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = "http://www.google.com";
Process ieProcess = Process.Start(startInfo);

This generates 2 processes visible in the Windows Task Manager. Then, I attempt to kill the process with:

ieProcess.Kill();

This results in one of the processes in Task Manager being shut down, and the other remains. I tried checking for any properties that would have children processes, but found none. How can I kill the other process also? More generally, how do you kill all the processes associated with a process that you start with Process.Start?

Liquor answered 5/5, 2011 at 17:17 Comment(4)
Maybe I'm missing something, but how come this starts two processes?Microscopium
@Etienne de Martel: IE uses several processes for its UI, basically 1 main process + 1 child process per open tab.Televise
@0xA3 Interesting, I assumed it simply worked like Firefox and had one process and a bunch of constantly hanging threads.Microscopium
thanks for your modified snippet/classIlianailine
A
90

This worked very nicely for me:

/// <summary>
/// Kill a process, and all of its children, grandchildren, etc.
/// </summary>
/// <param name="pid">Process ID.</param>
private static void KillProcessAndChildren(int pid)
{
    // Cannot close 'system idle process'.
    if (pid == 0)
    {
        return;
    }
    ManagementObjectSearcher searcher = new ManagementObjectSearcher
            ("Select * From Win32_Process Where ParentProcessID=" + pid);
    ManagementObjectCollection moc = searcher.Get();
    foreach (ManagementObject mo in moc)
    {
        KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
    }
    try
    {
        Process proc = Process.GetProcessById(pid);
        proc.Kill();
    }
    catch (ArgumentException)
    {
        // Process already exited.
    }
}

Update 2016-04-26

Tested on Visual Studio 2015 Update 2 on Win7 x64. Still works as well now as it did 3 years ago.

Update 2017-11-14

Added check for system idle process if (pid == 0)

Update 2018-03-02

Need to add a reference to the System.Management namespace, see comment from @MinimalTech below. If you have ReSharper installed, it will offer to do this for you automatically.

Update 2018-10-10

The most common use case for this is killing any child processes that our own C# process has started.

In this case, a better solution is to use Win32 calls within C# to make any spawned process a child process. This means that when the parent process exits, any child processes are automatically closed by Windows, which eliminates the need for the code above. Please let me know if you want me to post the code.

Ageratum answered 1/5, 2012 at 19:12 Comment(12)
What about grandchildren? Methinks this should be a recursive method.Rebuke
@Rebuke erm I think it isTanaka
What if E_ACCESSDENIED while listing processes?Feck
You are great!! Working like a charm with .NET 4.0 and VS2010. Just to note for someone (like me) that didn't know, you have to use System.Management namespace. But to be able to use it, you must go to your project name -> rightclick to it -> add reference -> .NET -> and select System.Management assembly as @Femaref note it here: #3692884Dichlorodifluoromethane
Warning, if you call this method regulary, it will lead to the wmiprvsv.exe consuming 40-100% of the CPU.Appleby
@Daniel Vygolov Good to know. Its probably the select statement that is doing it. Not sure why one would call this regularly, the only use cases I can think of are clean-up type operations. Wondering why one would need to call this regularly?Ageratum
@DanielVygolov Is there a better solution if I need to call this regularly?Unschooled
@Unschooled See my update above, is this applicable?Ageratum
Shouldn't it be InvalidOperationException instead of ArgumentException?Lissie
Perhaps - it depends on what exceptions can possibly by thrown, and if they should be ignored or not. If you are observing an InvalidOperationException, then it might make sense to add an extra handler. The exception might vary by the .NET framework installed, and might change with .NET Core. Both of these exceptions inherit from SystemException, so adding a handler for that might do the job as well.Ageratum
Note that Windows reuses Process Ids. If a process spawns child processes and quits leaving its children running, and then your process reuses the original process's id, then it will appear to have additional children. The above method will then attempt to kill these also (it may or may not have permission). One solution is to add a check of the time a process started and confirm it was after the root process in the tree to be killed.Three
One big issue that I have seen over the years after using this approach is that many customers have WMI corrupt on their machine and this code does not work in that case and throws a System.Management.ManagementException saying Invalid Class.Fanechka
I
35

If anyone needs a dotnet core solution,

Dotnet core 3.0

process.Kill(true);

See official documentation

Dotnet core 2.0

For .Net 2.0 dotnet cli came up with an implementation based on taskill as mentioned above and recursive pgrep/kill for unix based systems. Full implementation can be found on github. Sadly, the class is internal so you'll have to copy it into your code base.

List Child processes (has to be done recursively):

$"pgrep -P {parentId}"

Kill on process:

$"kill -TERM {processId}"
Isodynamic answered 14/8, 2017 at 4:56 Comment(2)
imho this should be the accepted answer, because dot net runs on linux as well.Lionhearted
this implementation for Dotnet core 2.0 saved my life hahaBeech
S
17

I'm not a fan of any of the solutions presented here.

Here's what I came up with:

private static void EndProcessTree(string imageName)
{
    Process.Start(new ProcessStartInfo
    {
        FileName = "taskkill",
        Arguments = $"/im {imageName} /f /t",
        CreateNoWindow = true,
        UseShellExecute = false
    }).WaitForExit();
}

How to use:

EndProcessTree("chrome.exe");
Sunlit answered 19/2, 2017 at 21:13 Comment(3)
From experience working on a compiling automation program, this will be very dangerous in a production environment where multiple processes are run in parallel. Simple example: Try to kill explorer.exe, or cmd.exe. A good portion of the user environment will evaporate.Macdermot
This didn't work at all. Process.GetProcessByName() returned the information was there, but calling this didn't fail, just didn't do anything.Ellery
Your code was copied and pasted. Passes chrome.exe like you did. Don't think taskkill is a standard windows utility and didn't fire on my windows 10 machine.Ellery
F
10

You should call Process.CloseMainWindow() which will send a message to the main window of the process. Think of it as having the user click the "X" close button or File | Exit menu item.

It is safer to send a message to Internet Explorer to close itself down, than go and kill all its processes. Those processes could be doing anything and you need to let IE do its thing and finish before just killing it in the middle of doing something that may be important for future runs. This goes true for any program you kill.

Fionnula answered 14/11, 2012 at 10:7 Comment(2)
I think this is the preferred way...for processes that have UIs.Liquor
The problem is that Process.CloseMainWindow() and Process.Close() don't always work.Ancohuma
L
9

If anyone is interested, I took one of the answers from the other page and modified it slightly. It is a self contained class now with static methods. It does not have proper error handling or logging. Modify to use for your own needs. Providing your root Process to KillProcessTree will do it.

class ProcessUtilities
{
    public static void KillProcessTree(Process root)
    {
        if (root != null)
        {
            var list = new List<Process>();
            GetProcessAndChildren(Process.GetProcesses(), root, list, 1);

            foreach (Process p in list)
            {
                try
                {
                    p.Kill();
                }
                catch (Exception ex)
                {
                    //Log error?
                }
            }
        }
    }

    private static int GetParentProcessId(Process p)
    {
        int parentId = 0;
        try
        {
            ManagementObject mo = new ManagementObject("win32_process.handle='" + p.Id + "'");
            mo.Get();
            parentId = Convert.ToInt32(mo["ParentProcessId"]);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
            parentId = 0;
        }
        return parentId;
    }

    private static void GetProcessAndChildren(Process[] plist, Process parent, List<Process> output, int indent)
    {
        foreach (Process p in plist)
        {
            if (GetParentProcessId(p) == parent.Id)
            {
                GetProcessAndChildren(plist, p, output, indent + 1);
            }
        }
        output.Add(parent);
    }
}
Liquor answered 7/5, 2011 at 15:25 Comment(0)
F
7

Another solution is to use the taskill command. I use the next code in my applications:

public static void Kill()
{
    try
    {
            ProcessStartInfo processStartInfo = new ProcessStartInfo("taskkill", "/F /T /IM your_parent_process_to_kill.exe")
            {
                WindowStyle = ProcessWindowStyle.Hidden,
                CreateNoWindow = true,
                UseShellExecute = false,
                WorkingDirectory = System.AppDomain.CurrentDomain.BaseDirectory,
                RedirectStandardOutput = true,
                RedirectStandardError = true
            };
            Process.Start(processStartInfo);
    }
    catch { }
}
Fortify answered 15/9, 2015 at 20:27 Comment(0)
B
4

Are you using IE8 or IE9? That would absolutely start more than one process due to its new multi-process architecture. Anyway, have a look at this other answer for getting a process tree and killing it.

Baeza answered 5/5, 2011 at 17:28 Comment(3)
Yes, this answers both why 2 processes start, and how to kill the process tree. Somehow I couldn't find this earlier, thanks.Liquor
Although, I have to admit, I was expecting it to be a lot simpler. Barf.Liquor
@robr: If the code works, it should be a simple copy and paste away from success.Baeza
M
3

Another approach that can be very useful is using the Windows API for Job Objects. A process can be assigned to a job object. The child processes of such a process are automatically assigned to the same job object.

All processes assigned to a job object can be killed at once e.g. with TerminateJobObject which:

Terminates all processes currently associated with the job.

The C# example in this answer (based on this answer) uses the JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag instead, which:

Causes all processes associated with the job to terminate when the last handle to the job is closed.

Malinin answered 17/5, 2017 at 9:42 Comment(0)
A
3

With .NET Core 3.0 there is a method just for that, namely new overload of the already existing Process.Kill() method. IOW, doing process.Kill(true) on the variable process of type Process kills the process with all its descendants. This is cross-platform, naturally.

Acetophenetidin answered 30/9, 2019 at 22:33 Comment(6)
Has this been verified as working correctly on Linux? The documentation (learn.microsoft.com/en-us/dotnet/api/…) refers to many Windows specifics.Resting
Further to previous comment, I found this (developers.redhat.com/blog/2019/10/29/…). Looks like things are badly crippled for Linux...Resting
@Resting Which part exactly tells that killing process tree is badly crippled for Linux?Acetophenetidin
Here is the commit that adds this feature, showing its cross-platform availability: github.com/dotnet/runtime/commit/… (Search for KillTree).Acetophenetidin
I was referring to the full functionality of the Process class as documented in that RedHat blog post.Resting
@Resting There are some caveats in the Process class in general, but the tree killing feature works well on Linux (as well as on other OSes).Acetophenetidin
P
1

As per documentation

The Kill method executes asynchronously. After calling the Kill method, call the WaitForExit method to wait for the process to exit, or check the HasExited property to determine if the process has exited.

ProcessStartInfo startInfo = new ProcessStartInfo("iexplore.exe");
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = "http://www.google.com";
Process ieProcess = Process.Start(startInfo);
ieProcess.Kill();
ieProcess.WaitForExit();
Pneumoconiosis answered 12/8, 2020 at 1:35 Comment(2)
Does this actually address the multiple threads problem though? It has been 9 years since I last dealt with this, so I may be forgetting, but not sure this would fix?Liquor
This addresses that even after you kill a process by calling Kill method , your routine may still pick it up if it calls GetProcessById because it didn't wait until the process exitsPneumoconiosis
P
0

How to properly close Internet Explorer when launched from PowerShell?

Several of those commented in the above thread that this is caused by a bug in Win7 (as it does not seem to occur for users that are using other versions of windows). Many pages on the internet, including microsoft's page claim user error, and tell you to simply use the available quit method on the IE object which is SUPPOSED to close all child processes as well (and reportedly does in Win8/XP etc)

I must admit, for my part, it WAS user error. I am in win7 and the reason the quit method was not working for me was because of an error in coding. Namely I was creating the IE object at declaration, and then creating another (attached to the same object) later on in the code... I had almost finished hacking the parent-child killing routine to work for me when I realized the issue.

Because of how IE functions, the processID you spawned as the parent could be attached to other windows/subprocesses that you did NOT create. Use quit, and keep in mind that depending on user settings (like empty cache on exit) it could take a few minutes for the processes to finish their tasks and close.

Prickett answered 20/11, 2015 at 19:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.