在 C# 中以编程方式杀死进程树

发布于 2024-11-05 10:52:55 字数 495 浏览 3 评论 0原文

我使用如下代码以编程方式启动 Internet Explorer:

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

这会生成 2 个在 Windows 任务管理器中可见的进程。然后,我尝试使用以下方法终止该进程:

ieProcess.Kill();

这会导致任务管理器中的一个进程被关闭,而另一个进程仍然存在。我尝试检查是否有任何具有子进程的属性,但没有找到。我怎样才能杀死其他进程?更一般地说,如何终止与使用 Process.Start 启动的进程关联的所有进程?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

似梦非梦 2024-11-12 10:52:56

我不喜欢这里提出的任何解决方案。

这是我的想法:

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

如何使用:

EndProcessTree("chrome.exe");

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");
浅浅 2024-11-12 10:52:56

您应该调用 Process.CloseMainWindow() ,它将向进程的主窗口发送一条消息。将其视为让用户单击“X”关闭按钮或文件 | 退出菜单项。

向 Internet Explorer 发送消息以关闭其自身比终止其所有进程更安全。这些进程可能正在执行任何操作,您需要让 IE 执行其操作并完成,然后再在执行对未来运行可能很重要的操作时终止它。这对于你杀死的任何程序都是如此。

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.

爱本泡沫多脆弱 2024-11-12 10:52:56

如果有人感兴趣,我从另一页上获取了一个答案并稍作修改。它现在是一个带有静态方法的自包含类。它没有有适当的错误处理或日志记录。修改以适合您自己的需要。将你的根进程提供给 KillProcessTree 就可以了。

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

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);
    }
}
蓝天 2024-11-12 10:52:56

另一种解决方案是使用taskill 命令。我在我的应用程序中使用以下代码:

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

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 { }
}
别再吹冷风 2024-11-12 10:52:56

您使用的是IE8还是IE9?由于其新的多进程架构,这绝对会启动多个进程。无论如何,看看这个其他获取进程树并杀死它的答案

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.

对你的占有欲 2024-11-12 10:52:56

另一种非常有用的方法是使用 Windows API作业对象。一个进程可以分配给一个作业对象。此类进程的子进程会自动分配给同一作业对象。

分配给作业对象的所有进程都可以立即终止,例如使用 TerminateJobObject 其中:

终止当前与作业关联的所有进程。

此答案中的 C# 示例 (基于此答案)使用 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 标志,其中:

当作业的最后一个句柄关闭时,导致与作业关联的所有进程终止。

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.

柒夜笙歌凉 2024-11-12 10:52:56

对于 .NET Core 3.0,有一种方法可以实现这一点,即 现有 Process.Kill() 方法的新重载。 IOW,对 Process 类型的变量 process 执行 process.Kill(true) 会杀死该进程及其所有后代。这自然是跨平台的。

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.

杀手六號 2024-11-12 10:52:56

根据文档

Kill 方法异步执行。调用 Kill 方法后,调用 WaitForExit 方法等待进程退出,或者检查 HasExited 属性以确定进程是否已退出。

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

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();
惯饮孤独 2024-11-12 10:52:56

从 PowerShell 启动时如何正确关闭 Internet Explorer?< /a>

一些人在上面的帖子中评论说这是由 Win7 中的错误引起的(因为使用其他版本 Windows 的用户似乎不会发生这种情况)。互联网上的许多页面,包括微软的页面都声称用户错误,并告诉您只需使用 IE 对象上的可用退出方法,该方法也应该关闭所有子进程(据报道在 Win8/XP 等中也是如此)

我必须承认,就我而言,这是用户错误。我使用的是win7,退出方法对我不起作用的原因是编码错误。也就是说,我在声明时创建了 IE 对象,然后在代码中创建了另一个(附加到同一对象)......当我意识到这个问题时,我几乎完成了对父子杀戮例程的破解工作。

由于 IE 的功能,您作为父进程生成的 processID 可能会附加到您未创建的其他窗口/子进程。使用退出,并记住,根据用户设置(例如退出时清空缓存),进程可能需要几分钟才能完成任务并关闭。

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.

时光瘦了 2024-11-12 10:52:55

这对我来说非常有效:

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

更新 2016-04-26

Win7 x64 上的 Visual Studio 2015 Update 2 上测试。现在仍然和 3 年前一样好用。

更新 2017-11-14

添加了对系统空闲进程的检查 if (pid == 0)

更新 2018-03-02

需要添加对 System.Management 命名空间的引用,请参阅下面来自 @MinimalTech 的评论。如果您安装了 ReSharper,它将自动为您执行此操作。

更新 2018-10-10

最常见的用例是杀死我们自己的 C# 进程已启动的任何子进程。

在这种情况下,更好的解决方案是在 C# 中使用 Win32 调用使任何生成的进程成为子进程。这意味着当父进程退出时,所有子进程都会被Windows自动关闭,这样就不再需要上面的代码了。如果您希望我发布代码,请告诉我。

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.

小忆控 2024-11-12 10:52:55

如果有人需要 dotnet core 解决方案,

Dotnet core 3.0

process.Kill(true);

请参阅官方文档

Dotnet core 2.0

对于.Net 2.0 dotnet cli 提出了一个基于taskill 如上所述,以及基于 UNIX 系统的递归 pgrep/kill可以找到完整的实现在 github 上。遗憾的是,该类是内部类,因此您必须将其复制到代码库中。

列出子进程(必须递归完成):

$"pgrep -P {parentId}"

杀死进程:

$"kill -TERM {processId}"

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}"
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文