Process.Kill()似乎不能杀死进程。

24

我在使用 Process.Kill() 时遇到了麻烦。我想我一定误解了它的工作原理。这是我的测试函数。我启动了一个长时间运行的进程 (ping -t),然后在五秒钟后将其杀死。

我可以看到 ping 进程出现了,但该进程在程序完成后仍然存在。我必须手动杀死它。

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);

我在这里做错了什么?


测试了你的代码,它运行得很好。你在哪里运行这段代码? - vesan
@vesan Windows 8.1。从PowerShell和cmd.exe两个环境中运行过。 - Kris Harper
2
@AdrianoRepetti 这似乎是问题所在。直接执行该进程似乎可以工作。你有任何想法为什么杀死cmd进程不起作用吗? - Kris Harper
2
因为Ping是cmd的子进程。你杀死了cmd,但它不会杀死所有的子进程,那么Ping仍然在运行。你可以杀死整个进程层次结构。另请参阅https://dev59.com/43A75IYBdhLWcg3wW3kZ。 - Adriano Repetti
除了之前的评论链接(如何可靠地终止进程)外,您可能需要使用查找进程的父级。几个注意事项:您可能不需要使用cmd.exe来运行子进程(如果不需要,则可以直接终止子进程);每个子进程可能都有子进程:您将拥有一个进程层次结构(您可能希望全部终止)。 - Adriano Repetti
显示剩余2条评论
6个回答

35

您启动了 cmd.exe,然后 cmd.exe 启动了子进程 ping.exe。要终止 ping.exe,您可以终止所有进程层次结构。例如,使用 WMI(添加 System.Management 引用):

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.)
        }
    }
}

2
如果子进程生成了自己的进程,怎么办?我们需要一些递归杀死整个进程树的方法,不是吗? - Kris Harper
在这段代码示例中,我们递归地杀死整棵树。 - SulNR
哦,奇怪,我之前阅读时可能错过了那部分。我会给你颁发赏金的。 - Kris Harper
父进程ID也可以被重复使用。因此,当您使用给定进程的父进程ID杀死所有内容时,您可以杀死在给定进程之前启动的进程。在这里,检查子进程的启动时间是否晚于父进程的启动时间至关重要。 - Alex des Pelagos
2
小心,这个答案应该更新为反转对 KillProcessAndChildrens 的递归调用和对 proc.Kill() 的调用,因为如果你在杀死父进程之前杀死子进程,你将会丢失子进程生成的进程,从而造成资源泄漏。请参考我的新建议。 - Julio

11

这是对@SulNR答案的一个补丁,因为它泄露了子进程的子进程。

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.
    }
}

4

process.Kill()已经能够运作了,只不过它并没有针对你想要杀死的进程进行操作。事实上,你正在启动两个进程,只杀死了第一个进程,而第二个进程仍在运行。 你的代码正在启动一个新的命令行并将该进程信息保存到process中。当你调用process.Kill()时,只有命令行退出了。你可以运行

Console.WriteLine(process.ProcessName);

在执行 process.Kill() 命令之前,你可以先查看将要被终止的进程。通过将 \c ping -t 8.8.8.8 作为参数传递给命令行,你告诉命令行启动另一个进程(在本例中是ping),并将其与自身分离。你的程序对子进程一无所知,也不会将其终止。如果你只想终止ping进程,可以将代码更改为:

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);

如果实在需要先启动命令行,您需要找到子进程并编写逻辑以终止它。类似这样的:

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

[编辑] 对不起,我一开始没有看到@Adriano Repetti的评论。我并不是想重复。


1

FWIW是Julio修改的VS2019 Visual Basic版本。

   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

0

非常简单:

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

使用此方法,您还可以终止具有更高保护级别的进程,例如taskmgr.exe。如果您想要防止某个进程启动,这里有一段适合的代码,至少在运行此代码的进程处于活动状态时是这样:

    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();
        };
    }

-2
为了终止一个进程,你必须使用管理员账户运行。这意味着你要么是一个“真正”的管理员,要么关闭用户账户控制(UAC)。否则,Process.Kill() 将会失败。
来自这里

当我回家后,我会尝试这个,但我相当确定我已经尝试过了,而且它没有起作用。只需要以管理员身份运行cmd.exe,然后调用我的程序,就足够了,对吗? - Kris Harper
我已经尝试在管理员提示符中运行我的测试程序,但似乎仍无法终止该进程。 - Kris Harper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接