24

I am having trouble using Process.Kill(). I think I must be misunderstanding how it works. This is my test function. I start a long-running process (ping -t) and then kill it five seconds later.

I can see the ping process show up, but the process is still there after my program finishes. I have to kill it manually.

Console.WriteLine("Total number of ping processes is {0}", Process.GetProcessesByName("ping").Length);

ProcessStartInfo startInfo = new ProcessStartInfo("cmd.exe");
Process process = new Process();

startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.Arguments = "/c ping -t 8.8.8.8";

Console.WriteLine("Staring ping process");
process.StartInfo = startInfo;
process.Start();
Thread.Sleep(5000);

Console.WriteLine("Total number of ping processes is {0}", Process.GetProcessesByName("ping").Length);
Thread.Sleep(5000);

Console.WriteLine("Killing ping process");
process.Kill();
Thread.Sleep(5000);

Console.WriteLine("Total number of ping processes is {0}", Process.GetProcessesByName("ping").Length);

What am I doing wrong here?

Kris Harper
  • 5,672
  • 8
  • 51
  • 96
  • Tested your code and it works fine. Where are you running this code from? – vesan May 15 '15 at 01:06
  • @vesan Windows 8.1. Ran it from both PowerShell and cmd.exe. – Kris Harper May 15 '15 at 01:11
  • Any difference to directly execute Ping.exe without cmd? – Adriano Repetti May 17 '15 at 16:55
  • 2
    @AdrianoRepetti This seems to be the issue. Executing the process directly seems to work. Any idea why killing the cmd process doesn't work? – Kris Harper May 17 '15 at 17:11
  • 2
    because Ping is child process of cmd. You kill cmd but it won't also kill all children then Ping is still running. You may kill all process hierarchy. See also http://stackoverflow.com/q/3342941/1207195 – Adriano Repetti May 17 '15 at 17:40
  • In addition to previous comment link ([how to kill a process in a reliable way](http://stackoverflow.com/q/3342941/1207195)) you may use need to [find process' parent](http://stackoverflow.com/a/3346055/1207195). Few notes: you may not need to use cmd.exe to run a child process (if you don't then you can kill child process directly); each child process may have children: what you'll have is a processes hierarchy (and you may want to [kill 'em all](https://www.youtube.com/watch?v=aAITxlCsj4Y)). – Adriano Repetti May 20 '15 at 10:34
  • This happen to me as well. For me it killed the process after all logic ran. Basically I had to return after the call and make sure that "nothing" after the call is being executed. As if the application would wait for some spare time to kill itself. This is certainly a workaround and not a fix. – Noel Widmer May 21 '15 at 09:41

6 Answers6

35

You started cmd.exe, then cmd.exe starts child process ping.exe. To kill ping.exe you can kill all process hierarchy. For example with WMI(add System.Management reference):

private static void KillProcessAndChildrens(int pid)
{
    ManagementObjectSearcher processSearcher = new ManagementObjectSearcher
      ("Select * From Win32_Process Where ParentProcessID=" + pid);
    ManagementObjectCollection processCollection = processSearcher.Get();

    try
    {
        Process proc = Process.GetProcessById(pid);
        if (!proc.HasExited) proc.Kill();
    }
    catch (ArgumentException)
    {
        // Process already exited.
    }

    if (processCollection != null)
    {
        foreach (ManagementObject mo in processCollection)
        {
            KillProcessAndChildrens(Convert.ToInt32(mo["ProcessID"])); //kill child processes(also kills childrens of childrens etc.)
        }
    }
}
SulNR
  • 823
  • 7
  • 14
  • 2
    What if the child process spawns a process of its own? We would need some way to recursively kill the whole tree, no? – Kris Harper May 21 '15 at 14:26
  • In this sample of code we kill whole tree recursively. – SulNR May 21 '15 at 15:04
  • Oh weird, I must have missed that when I read it earlier. I'll award you the bounty. – Kris Harper May 21 '15 at 15:35
  • Parent PID can also be re-used. So when you kill everything with the parent PID as the pid of the given process you can kill processes started before the given one. Checking that the start time of the child is after start time of the parent is crucial here. – Alex des Pelagos Oct 30 '15 at 18:52
  • 2
    Be careful, this answer should be updated to invert the recursive call to `KillProcessAndChildrens` and the call to `proc.Kill()` because if you kill parents before children you just loose children spawned processes you will leak them. See my new proposal. – Julio Oct 26 '16 at 14:47
11

This is a patch for the @SulNR answer since its answer leak child processes of child processes.

private static void KillProcessAndChildrens(int pid)
{
    ManagementObjectSearcher processSearcher = new ManagementObjectSearcher
      ("Select * From Win32_Process Where ParentProcessID=" + pid);
    ManagementObjectCollection processCollection = processSearcher.Get();

    // We must kill child processes first!
    if (processCollection != null)
    {
        foreach (ManagementObject mo in processCollection)
        {
            KillProcessAndChildrens(Convert.ToInt32(mo["ProcessID"])); //kill child processes(also kills childrens of childrens etc.)
        }
    }

    // Then kill parents.
    try
    {
        Process proc = Process.GetProcessById(pid);
        if (!proc.HasExited) proc.Kill();
    }
    catch (ArgumentException)
    {
        // Process already exited.
    }
}
Community
  • 1
  • 1
Julio
  • 339
  • 2
  • 6
4

process.Kill() is working, just not on the process you think. What you're doing is actually starting 2 processes and only killing the first process, while the second process keeps running. The code you have is starting a new command shell and saving that process info to process. When you call process.Kill() only the command shell is exiting You can run

Console.WriteLine(process.ProcessName);

before you process.Kill() to see which process is actually going to be killed. By setting \c ping -t 8.8.8.8 as arguments to the command shell, you're telling the command shell to start another process (in this case ping) and disjoin it from itself. Your program has no knowledge about the child process and will not kill it. If all you really want is to kill the ping process you can change your code to:

Console.WriteLine("Total number of ping processes is {0}",  Process.GetProcessesByName("ping").Length);

ProcessStartInfo startInfo = new ProcessStartInfo("ping");
Process process = new Process();

startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.Arguments = "-t 8.8.8.8";

Console.WriteLine("Staring ping process");
process.StartInfo = startInfo;
process.Start();
Thread.Sleep(5000);

Console.WriteLine("Total number of ping processes is {0}", Process.GetProcessesByName("ping").Length);
Thread.Sleep(5000);

Console.WriteLine("Killing ping process");
process.Kill();
Thread.Sleep(5000);

Console.WriteLine("Total number of ping processes is {0}", Process.GetProcessesByName("ping").Length);

If, however, you really need to start the command shell first you'll need to find the child processes and have logic to kill that. Something like:

foreach( var p in Process.GetProcessesByName("ping"))
{
  p.Kill();
}

[EDIT] *Sorry, I didn't see the comments from @Adriano Repetti at first. I didn't mean to be redundant.

rogergarrison
  • 1,224
  • 7
  • 7
1

FWIW a VS2019 Visual Basic version of Julio's modification.

   Private Sub KillProcessAndChildrens(pintPID As Integer)

      Dim processSearcher As New ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + pintPID.ToString)

      Dim processCollection As ManagementObjectCollection = processSearcher.Get()
      '     We must kill child processes first!
      If Not IsNothing(processCollection) Then
         For Each mo As ManagementObject In processCollection
            KillProcessAndChildrens(Convert.ToInt32(mo.Item("ProcessID")))
         Next
      End If

      '    // Then kill parents.
      Try
         Dim proc As Process = Process.GetProcessById(pintPID)
         If Not proc.HasExited Then
            proc.Kill()
         End If

      Catch ex As Exception
         '         Process already exited.
      End Try
   End Sub
AndruWitta
  • 186
  • 3
  • 6
0

It's very simple:

foreach (var process in Process.GetProcessesByName(processName))
{
    try
    {
       process.Kill();
    }
    catch { }
}

With this method, you can also kill processes with a higher protection level like taskmgr.exe. If you want to prevent a process from starting, here is a piece of code suitable for that, at least as long as the process running this code is active:

    private void Disable(string processName)
    {
        var timer = new Timer()
        {
            Interval = 1,
            Enabled = true
        };
        timer.Tick += (s, e) =>
        {
            foreach (var process in Process.GetProcessesByName(processName))
            {
                try
                {
                    process.Kill();
                }
                catch { }
            }

            GC.Collect();
        };
    }
Iavor Orlyov
  • 512
  • 1
  • 4
  • 15
-2

In order to kill a process you have to run under an administrative account. This means either that you are a 'true' administrator or you User Account Control (UAC) turned off.

Otherwise Process.Kill() will fail.

From here.

Community
  • 1
  • 1
Matthew Mage
  • 395
  • 5
  • 18
  • I will try this when I get home, but I'm pretty sure I already tried and it didn't work. It should be enough to run cmd.exe as an administrator and then call my program, right? – Kris Harper May 15 '15 at 17:19
  • I've tried running my test program in an administrator prompt, and it still doesn't seem to kill the process. – Kris Harper May 16 '15 at 00:56