Pergunta

Estou iniciando o Internet Explorer programaticamente com um código semelhante a este:

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

Isso gera 2 processos visíveis no Gerenciador de Tarefas do Windows.Então, tento encerrar o processo com:

ieProcess.Kill();

Isso resulta no encerramento de um dos processos do Gerenciador de Tarefas e o outro permanece.Tentei verificar se havia alguma propriedade que tivesse processos filhos, mas não encontrei nenhuma.Como posso matar o outro processo também?De maneira mais geral, como você elimina todos os processos associados a um processo iniciado com Process.Start?

Foi útil?

Solução

Isso funcionou muito bem para mim:

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

Atualização 26/04/2016

Testado em Visual Studio 2015 Update 2 sobre Win7 x64.Ainda funciona tão bem agora como há 3 anos.

Atualização 14/11/2017

Adicionada verificação de processo ocioso do sistema if (pid == 0)

Atualização 02/03/2018

Precisa adicionar uma referência ao System.Management namespace, veja o comentário de @MinimalTech abaixo.Se você tiver o ReSharper instalado, ele se oferecerá para fazer isso automaticamente.

Atualização 10/10/2018

O caso de uso mais comum para isso é eliminar qualquer processo filho que nosso próprio processo C# tenha iniciado.

Nesse caso, uma solução melhor é usar chamadas Win32 em C# para tornar qualquer processo gerado um processo filho.Isso significa que quando o processo pai é encerrado, todos os processos filhos são fechados automaticamente pelo Windows, o que elimina a necessidade do código acima.Por favor, deixe-me saber se você deseja que eu poste o código.

Outras dicas

Não sou fã de nenhuma das soluções apresentadas aqui.

Aqui está o que eu descobri:

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

Como usar:

EndProcessTree("chrome.exe");

Se alguém precisa de uma solução central dotnet, dotnet cli veio com uma implementação baseada em taskill como mencionado acima e recursiva pgrep / kill para sistemas baseados em Unix. Implementação completa pode ser encontradano github .Infelizmente, a classe é interna, então você terá que copiá-la em sua base de código.

Listar processos filho (deve ser feito recursivamente):

$"pgrep -P {parentId}"

Kill no processo:

$"kill -TERM {processId}"

Você deve chamar Process.CloseMainWindow() que enviará uma mensagem para a janela principal do processo.Pense nisso como se o usuário clicasse no botão "X" para fechar ou Arquivo | Sair do item de menu.

É mais seguro enviar uma mensagem para o Internet Explorer para fechar-se, do que ir e matar todos os seus processos.Esses processos podem estar fazendo qualquer coisa e você precisa deixar o IE fazer seu trabalho e terminar antes de simplesmente matá-lo no meio de algo que pode ser importante para execuções futuras.Isso vale para qualquer programa que você mata.

Se alguém estiver interessado, peguei uma das respostas da outra página e modifiquei um pouco.Agora é uma classe independente com métodos estáticos.Ele não possui gerenciamento ou registro de erros adequado.Modifique para usar de acordo com suas necessidades.Fornecer seu processo raiz para KillProcessTree fará isso.

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

Outra solução é usar o comando taskill.Eu uso o próximo código em meus aplicativos:

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

Você está usando o IE8 ou o IE9?Isso com certeza iniciaria mais de um processo devido à sua nova arquitetura de multiprocessos.De qualquer forma, dê uma olhada em este outroresposta por obter uma árvore de processo e eliminá-la.

Outra abordagem que pode ser muito útil é usar o API do Windows para objetos de trabalho . Um processo pode ser atribuído a um objeto de trabalho. Os processos filho de tal processo são atribuídos automaticamente ao mesmo objeto de trabalho.

Todos os processos atribuídos a um objeto de trabalho podem ser eliminados de uma vez, por exemplo, com TerminateJobObject que :

Termina todos os processos atualmente associados ao trabalho.

O exemplo C # nesta resposta ( com base nesta resposta ) usa o JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE em vez disso, que:

Faz com que todos os processos associados ao trabalho sejam encerrados quando o último identificador do trabalho for fechado.

Como fechar corretamente o Internet Explorer quando iniciado de PowerShell?

Vários dos que comentaram no tópico acima que isso é causado por um bug no Win7 (já que não parece ocorrer para usuários que estão usando outras versões do Windows). Muitas páginas na Internet, incluindo a página da microsoft, afirmam o erro do usuário e dizem a você para simplesmente usar o método quit disponível no objeto do IE, que SUPOSTA fechará também todos os processos filho (e supostamente fecha no Win8 / XP etc)

Devo admitir, de minha parte, foi um erro do usuário. Estou no win7 e o motivo pelo qual o método de parada não estava funcionando para mim foi um erro na codificação. Ou seja, eu estava criando o objeto IE na declaração e, em seguida, criando outro (anexado ao mesmo objeto) mais tarde no código ... Eu quase terminei de hackear a rotina de assassinato pai-filho para funcionar para mim quando percebi o problema.

Por causa de como o IE funciona, o processID que você gerou como pai pode ser anexado a outras janelas / subprocessos que você NÃO criou. Use quit e lembre-se de que, dependendo das configurações do usuário (como cache vazio ao sair), pode levar alguns minutos para que os processos concluam suas tarefas e fechem.

Com o .NET Core 3.0, existe um método exatamente para isso, a saber nova sobrecarga do método Process.Kill() já existente.IOW, fazer process.Kill(true) na variável process do tipo Process mata o processo com todos os seus descendentes.Isso é multiplataforma, naturalmente.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top