web-dev-qa-db-fra.com

Supprimer l'arborescence des processus par programmation en C #

Je lance Internet Explorer par programme avec un code qui ressemble à ceci:

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

Cela génère 2 processus visibles dans le gestionnaire de tâches Windows. Ensuite, j'essaie de tuer le processus avec:

ieProcess.Kill();

Cela entraîne l'arrêt de l'un des processus du gestionnaire de tâches et le maintien de l'autre. J'ai essayé de vérifier toutes les propriétés qui auraient des processus enfants, mais n'en ai trouvé aucun. Comment puis-je tuer l'autre processus aussi? Plus généralement, comment tuer tous les processus associés à un processus que vous démarrez avec Process.Start?

34
robr

Cela a très bien fonctionné pour moi:

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

Mise à jour 2016-04-26

Testé sur Visual Studio 2015 Update 2 sur Win7 x64. Fonctionne toujours aussi bien maintenant qu'il y a trois ans.

Mise à jour 2017-11-14

Ajout de la vérification du processus inactif du système if (pid == 0)

Mise à jour 2018-03-02

Besoin d'ajouter une référence à l'espace de noms System.Management, voir le commentaire de @MinimalTech ci-dessous. Si vous avez installé ReSharper, il vous proposera de le faire automatiquement pour vous.

Mise à jour 2018-10-10

Le cas d'utilisation le plus courant consiste à supprimer tous les processus enfants démarrés par notre propre processus C #. 

Dans ce cas, une meilleure solution consiste à utiliser les appels Win32 dans C # pour que tout processus généré devienne un processus enfant. Cela signifie que lorsque le processus parent est fermé, tous les processus enfants sont automatiquement fermés par Windows, ce qui élimine la nécessité du code ci-dessus. S'il vous plaît laissez-moi savoir si vous voulez que je poste le code.

54
Contango

Vous devez appeler Process.CloseMainWindow() qui enverra un message à la fenêtre principale du processus. Pensez-y comme si l'utilisateur cliquait sur le bouton de fermeture "X" ou sur Fichier | Quitter élément de menu.

Il est plus sûr d’envoyer un message à Internet Explorer pour qu’il se ferme, au lieu de tuer tous ses processus. Ces processus pourraient faire n'importe quoi et vous devez laisser IE faire son travail et terminer avant de simplement le tuer en train de faire quelque chose qui pourrait être important pour les futures séries. Cela est vrai pour tout programme que vous supprimez.

10
Kerry Kobashi

Je ne suis pas fan des solutions présentées ici.

Voici ce que j'ai trouvé:

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

Comment utiliser:

EndProcessTree("chrome.exe");
8
Owen

Si quelqu'un est intéressé, j'ai pris l'une des réponses de l'autre page et l'ai légèrement modifiée. C'est une classe autonome maintenant avec des méthodes statiques. Not ne gère pas correctement les erreurs ou n'enregistre pas. Modifier pour utiliser pour vos propres besoins. Fournir votre processus racine à KillProcessTree le fera.

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

Une autre solution consiste à utiliser la commande taskill. J'utilise le code suivant dans mes 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 { }
}
6
Juan Carlos Velez

Si quelqu'un a besoin d'une solution de base dotnet, dotnet cli propose une implémentation basée sur taskill, comme mentionné ci-dessus, et la méthode récursive pgrep/kill pour les systèmes Unix. L’implémentation complète est disponible sur github . Malheureusement, la classe est interne, vous devrez donc la copier dans votre base de code.

Lister les processus enfants (doit être fait de manière récursive):

$"pgrep -P {parentId}"

Tuez le processus:

$"kill -TERM {processId}"
6
Cyprien Autexier

Utilisez-vous IE8 ou IE9? Cela lancerait absolument plus d'un processus en raison de sa nouvelle architecture multi-processus. Quoi qu'il en soit, jetez un œil à cette autre réponse pour obtenir un arbre de processus et le tuer.

4

Une autre approche qui peut être très utile consiste à utiliser l’API Windows pour les objets de travail . Un processus peut être affecté à un objet de travail. Les processus enfants d'un tel processus sont automatiquement affectés au même objet de travail.

Tous les processus affectés à un objet de travail peuvent être supprimés en même temps, par exemple. avec TerminateJobObject qui:

Termine tous les processus actuellement associés au travail. 

L'exemple C # dans cette réponse ( basé sur cette réponse ) utilise le JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag à la place, qui:

Arrête tous les processus associés au travail lorsque le dernier descripteur du travail est fermé.

2
Peter

Comment fermer correctement Internet Explorer lorsqu'il est lancé à partir de PowerShell?

Plusieurs de ceux qui ont commenté dans le fil de discussion ci-dessus que cela est dû à un bogue dans Win7 (comme cela ne semble pas se produire pour les utilisateurs qui utilisent d'autres versions de Windows). De nombreuses pages sur Internet, y compris l'erreur de page de Microsoft, vous invitent à utiliser simplement la méthode de fermeture disponible sur l'objet IE qui est SUPPOSÉ pour fermer également tous les processus enfants (et le ferait dans Win8/XP, etc. )

Je dois admettre, pour ma part, qu'il s'agissait d'une erreur de l'utilisateur. Je suis dans win7 et la raison pour laquelle la méthode d'abandon du tabac ne fonctionnait pas pour moi était due à une erreur de codage. A savoir, je créais l'objet IE lors de la déclaration, puis un autre (attaché au même objet) plus tard dans le code ... J'avais presque fini de pirater la routine de mise à mort parent-enfant pour qu'elle fonctionne pour moi lorsque réalisé le problème.

En raison de la manière dont IE fonctionne, l'ID de processus que vous avez créé en tant que parent pourrait être lié à d'autres fenêtres/sous-processus que vous n'avez PAS créés. Utilisez la commande quit et gardez à l'esprit que, selon les paramètres de l'utilisateur (tels que le cache vide à la sortie), les processus peuvent mettre quelques minutes à terminer leurs tâches et à se fermer.

0
Jack Hadley