基本上,子进程无限期地运行直到在后台被杀死,并且当我的程序因任何原因终止时,我想要清理它,即通过Taskmanager。
目前我有一段时间(Process.GetProcessesByName(“ParentProcess”)。Count()> 0)循环并退出,如果父进程没有运行,但它看起来很脆弱,如果我希望它工作在Visual Studio中的调试器下,我必须添加“ParentProcess.vshost”或其他东西。
有没有办法确保子进程在不要求子进程知道父进程的情况下结束?我更喜欢托管代码中的解决方案,但如果没有,我可以PInvoke。
编辑:传递PID似乎是一个更强大的解决方案,但出于好奇,如果子进程不是我的代码而是一些我无法控制的exe怎么办?有没有办法防止可能创建孤立的子进程?
答案 0 :(得分:7)
如果子进程是您自己的代码,则可以在启动它时将其传递给父进程的PID。然后,子进程可以使用Process.GetProcessById
获取进程,并使用处理程序订阅其Exited
事件,该处理程序可以正常关闭(子)进程的其余部分。请注意,您需要将流程上的EnableRaisingEvents
属性设置为true
。
答案 1 :(得分:5)
如果子进程不是您自己的代码,您可以使用此代码查找并终止所有子进程:
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Util {
public static class ProcessExtensions {
public static void KillDescendants(this Process processToNotKillYet) {
foreach (var eachProcess in Process.GetProcesses()) {
if (eachProcess.ParentPid() == processToNotKillYet.Id) {
eachProcess.KillTree();
}
}
}
public static void KillTree(this Process processToKill) {
processToKill.KillDescendants();
processToKill.Kill();
}
public static PROCESS_BASIC_INFORMATION Info(this Process process) {
var processInfo = new PROCESS_BASIC_INFORMATION();
try {
uint bytesWritten;
NtQueryInformationProcess(process.Handle,
0,
ref processInfo,
(uint)Marshal.SizeOf(processInfo),
out bytesWritten); // == 0 is OK
}
catch (Win32Exception e) {
if (!e.Message.Equals("Access is denied")) throw;
}
return processInfo;
}
public static int ParentPid(this Process process) {
return process.Info().ParentPid;
}
[DllImport("ntdll.dll")]
private static extern int NtQueryInformationProcess(
IntPtr hProcess,
int processInformationClass /* 0 */,
ref PROCESS_BASIC_INFORMATION processBasicInformation,
uint processInformationLength,
out uint returnLength);
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_BASIC_INFORMATION {
public int ExitStatus;
public int PebBaseAddress;
public int AffinityMask;
public int BasePriority;
public int Pid;
public int ParentPid;
}
}
}
答案 2 :(得分:3)
orphan process中此类子进程的常用术语。有关可能的解决方案,请参阅链接文章。
答案 3 :(得分:1)
这是我构建的小型实用程序应用程序的源代码 (它基于Alan Hensel解决方案,我发现它非常有用)。
它被称为ChildrenProcessKiller,它是一个观察程序,可以在父进程退出时杀死给定父进程的所有后代进程(即使父进程崩溃)
用法:
ChildrenProcessKiller.exe parentProcessId
警告:此代码“按原样”提供,可能会杀死小宝宝;-)
ChildrenProcessKiller.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ChildrenProcessKiller
{
static class ChildrenProcessKiller
{
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
string message = "This is a watcher that enables to kill all descendants process of a given parent process\n";
message += "when the parent process exits (even if the parent process crashes) \n\n";
message += "Usage : " + Application.ExecutablePath + " parentProcessId";
if (args.Length != 1)
{
MessageBox.Show(message);
System.Environment.Exit(1);
}
int parentProcessId;
if (!Int32.TryParse(args[0], out parentProcessId))
{
MessageBox.Show(message);
System.Environment.Exit(1);
}
try
{
mParentProcess = Process.GetProcessById(parentProcessId);
}
catch (System.ArgumentException ex)
{
//Parent process cannot be found!
System.Environment.Exit(2);
}
Run();
}
private static List<Process> mChildrenProcesses;
private static Process mParentProcess;
private static void Run()
{
int thisProcessId = Process.GetCurrentProcess().Id;
while ( ! mParentProcess.HasExited )
{
RefreshChildrenProcesses();
System.Threading.Thread.Sleep(1000);
}
foreach (Process childProcess in mChildrenProcesses)
{
if ((!childProcess.HasExited) && (childProcess.Id != thisProcessId))
{
KillGracefullyThenViolently(childProcess);
}
}
}
private static void KillGracefullyThenViolently(Process process)
{
if (process.HasExited)
return;
try
{
process.CloseMainWindow();
}
catch (PlatformNotSupportedException)
{}
catch (InvalidOperationException)
{}//do nothing : this app is meant to be "unstoppable", unless the parent process has exited
for (int i = 0; i < 15; i++)
{
System.Threading.Thread.Sleep(100);
if (process.HasExited)
return;
}
try
{
process.Kill();
}
catch (System.ComponentModel.Win32Exception)
{}
catch(NotSupportedException)
{}
catch(InvalidOperationException)
{} //same comment here
}
private static void RefreshChildrenProcesses()
{
if (mParentProcess.HasExited)
return;
List<Process> newChildren;
try
{
newChildren = Utils.ProcessTree.GetProcessDescendants(mParentProcess);
mChildrenProcesses = newChildren;
}
catch (System.Exception ex)
{
;
}
}
}
}
ProcessTree.cs
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
namespace Utils
{
public static class ProcessTree
{
public static List<Process> GetProcessDescendants(Process process)
{
List<Process> result = new List<Process>();
foreach (Process eachProcess in Process.GetProcesses())
{
if (ParentPid(eachProcess) == process.Id)
{
result.Add(eachProcess);
}
}
return result;
}
public static void KillDescendants(Process processToNotKillYet)
{
foreach (Process eachProcess in Process.GetProcesses())
{
if (ParentPid(eachProcess) == processToNotKillYet.Id)
{
if (eachProcess.Id != Process.GetCurrentProcess().Id)
KillTree(eachProcess);
}
}
}
public static void KillTree(Process processToKill)
{
KillDescendants(processToKill);
processToKill.Kill();
}
public static PROCESS_BASIC_INFORMATION Info(Process process)
{
PROCESS_BASIC_INFORMATION processInfo = new PROCESS_BASIC_INFORMATION();
try
{
uint bytesWritten;
NtQueryInformationProcess(process.Handle,
0,
ref processInfo,
(uint)Marshal.SizeOf(processInfo),
out bytesWritten); // == 0 is OK
}
catch (Win32Exception e)
{
if (!e.Message.Equals("Access is denied")) throw;
}
return processInfo;
}
public static int ParentPid(Process process)
{
return Info(process).ParentPid;
}
[DllImport("ntdll.dll")]
private static extern int NtQueryInformationProcess(
IntPtr hProcess,
int processInformationClass /* 0 */,
ref PROCESS_BASIC_INFORMATION processBasicInformation,
uint processInformationLength,
out uint returnLength);
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_BASIC_INFORMATION
{
public int ExitStatus;
public int PebBaseAddress;
public int AffinityMask;
public int BasePriority;
public int Pid;
public int ParentPid;
}
}
}
答案 4 :(得分:0)
将父进程id作为命令行参数传递给子进程。
在子进程中使用get by id并订阅它的Exit事件或创建一个线程并调用Process.WaitForExit