当父进程被终止时,终止子进程
我从我的应用程序中使用System.Diagnostics.Process
类创建新的进程。
我希望当/如果我的应用程序崩溃,这个进程被杀害。 但是,如果我从任务管理器杀死我的应用程序,子进程不会被终止。
有没有办法让子进程依赖父进程?
从这个论坛上 ,信用“乔希”。
Application.Quit()
和Process.Kill()
是可能的解决方案,但已被证明是不可靠的。 当你的主应用程序死亡,你仍然留下子进程运行。 我们真正想要的是一旦主流程死亡,子进程就会死掉。
解决方案是使用“作业对象” http://msdn.microsoft.com/en-us/library/ms682409(VS.85).aspx 。
这个想法是为您的主应用程序创建一个“作业对象”,并将您的子进程注册到作业对象。 如果主进程死了,操作系统将负责终止子进程。
public enum JobObjectInfoType { AssociateCompletionPortInformation = 7, BasicLimitInformation = 2, BasicUIRestrictions = 4, EndOfJobTimeInformation = 6, ExtendedLimitInformation = 9, SecurityLimitInformation = 5, GroupInformation = 11 } [StructLayout(LayoutKind.Sequential)] public struct SECURITY_ATTRIBUTES { public int nLength; public IntPtr lpSecurityDescriptor; public int bInheritHandle; } [StructLayout(LayoutKind.Sequential)] struct JOBOBJECT_BASIC_LIMIT_INFORMATION { public Int64 PerProcessUserTimeLimit; public Int64 PerJobUserTimeLimit; public Int16 LimitFlags; public UInt32 MinimumWorkingSetSize; public UInt32 MaximumWorkingSetSize; public Int16 ActiveProcessLimit; public Int64 Affinity; public Int16 PriorityClass; public Int16 SchedulingClass; } [StructLayout(LayoutKind.Sequential)] struct IO_COUNTERS { public UInt64 ReadOperationCount; public UInt64 WriteOperationCount; public UInt64 OtherOperationCount; public UInt64 ReadTransferCount; public UInt64 WriteTransferCount; public UInt64 OtherTransferCount; } [StructLayout(LayoutKind.Sequential)] struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION { public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; public IO_COUNTERS IoInfo; public UInt32 ProcessMemoryLimit; public UInt32 JobMemoryLimit; public UInt32 PeakProcessMemoryUsed; public UInt32 PeakJobMemoryUsed; } public class Job : IDisposable { [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] static extern IntPtr CreateJobObject(object a, string lpName); [DllImport("kernel32.dll")] static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength); [DllImport("kernel32.dll", SetLastError = true)] static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process); private IntPtr m_handle; private bool m_disposed = false; public Job() { m_handle = CreateJobObject(null, null); JOBOBJECT_BASIC_LIMIT_INFORMATION info = new JOBOBJECT_BASIC_LIMIT_INFORMATION(); info.LimitFlags = 0x2000; JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION(); extendedInfo.BasicLimitInformation = info; int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length); Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); if (!SetInformationJobObject(m_handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length)) throw new Exception(string.Format("Unable to set information. Error: {0}", Marshal.GetLastWin32Error())); } #region IDisposable Members public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion private void Dispose(bool disposing) { if (m_disposed) return; if (disposing) {} Close(); m_disposed = true; } public void Close() { Win32.CloseHandle(m_handle); m_handle = IntPtr.Zero; } public bool AddProcess(IntPtr handle) { return AssignProcessToJobObject(m_handle, handle); } }
看着构造函数…
JOBOBJECT_BASIC_LIMIT_INFORMATION info = new JOBOBJECT_BASIC_LIMIT_INFORMATION(); info.LimitFlags = 0x2000;
这里的关键是正确设置作业对象。 在构造函数中,我将“limits”设置为0x2000,这是JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
的数字值。
MSDN将此标志定义为:
导致与作业关联的所有进程在作业的最后一个句柄关闭时终止。
一旦这个课程设置好了,你只需要注册每个子进程。 例如:
[DllImport("user32.dll", SetLastError = true)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); Excel.Application app = new Excel.ApplicationClass(); uint pid = 0; Win32.GetWindowThreadProcessId(new IntPtr(app.Hwnd), out pid); job.AddProcess(Process.GetProcessById((int)pid).Handle);
这篇文章的目的是作为@Matt Howells的答案的扩展,特别是那些遇到在Vista或Win7下使用Job Objects时出现问题的人,尤其是在调用AssignProcessToJobObject时遇到访问被拒绝错误('5')的情况。
TL;博士
为确保与Vista和Win7兼容,请将以下清单添加到.NET父进程中:
<?xml version="1.0" encoding="utf-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <v3:trustInfo xmlns:v3="urn:schemas-microsoft-com:asm.v3"> <v3:security> <v3:requestedPrivileges> <v3:requestedExecutionLevel level="asInvoker" uiAccess="false" /> </v3:requestedPrivileges> </v3:security> </v3:trustInfo> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <!-- We specify these, in addition to the UAC above, so we avoid Program Compatibility Assistant in Vista and Win7 --> <!-- We try to avoid PCA so we can use Windows Job Objects --> <!-- See https://stackoverflow.com/questions/3342941/kill-child-process-when-parent-process-is-killed --> <application> <!--The ID below indicates application support for Windows Vista --> <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> <!--The ID below indicates application support for Windows 7 --> <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> </application> </compatibility> </assembly>
请注意,当您在Visual Studio 2012中添加新清单时,它将包含上面的代码片段,因此您不需要将其复制。 它还将包括一个Windows 8的节点。
完整的解释
如果您正在启动的进程已经与另一个作业相关联,则您的作业关联将失败并显示访问被拒绝错误。 进入程序兼容性助手,从Windows Vista开始,将把各种进程分配给自己的作业。
在Vista中,您可以通过简单地包含应用程序清单来将您的应用程序标记为从PCA中排除。 Visual Studio似乎自动执行.NET应用程序,所以你很好。
一个简单的清单不再削减在Win7中。 [1]在那里,你必须特别指定你与清单中的标签兼容Win7。 [2]
这让我担心Windows 8.我将不得不再次改变我的清单吗? 显然,在云端有一个突破,因为Windows 8现在允许一个进程属于多个作业。 [3]所以我还没有测试过,但是我想如果你只是简单地包含一个带有受支持的操作系统信息的清单,那么这个疯狂就会结束了。
技巧1 :如果您正在使用Visual Studio开发.NET应用程序,那么这里[4]是关于如何自定义应用程序清单的一些很好的说明。
提示2 :小心从Visual Studio启动应用程序。 我发现,在添加适当的清单后,即使我使用“无调试开始”,从Visual Studio启动时仍然遇到PCA问题。 但是,从Explorer启动我的应用程序工作。 在使用注册表手动添加devenv以从PCA中排除之后,启动使用VS中Job对象的应用程序也开始工作。 [5]
提示3 :如果您想知道PCA是否是您的问题,请尝试从命令行启动您的应用程序,或将程序复制到网络驱动器并从那里运行。 在这些情况下PCA被自动禁用。
[1] http://blogs.msdn.com/b/cjacks/archive/2009/06/18/pca-changes-for-windows-7-how-to-tell-us-you-are-not-an卸载器通吃2 -因为,我们改变的,在规则上,you.aspx
[2] http://ayende.com/blog/4360/how-to-opt-out-of-program-compatibility-assistant
[3] http://msdn.microsoft.com/en-us/library/windows/desktop/ms681949(v=vs.85).aspx :“进程可以与Windows 8中的多个作业关联”
[4] 如何将应用程序清单嵌入到使用VS2008的应用程序中?
[5] 如何停止Visual Studio调试器在作业对象中启动我的过程?
这个答案从@Matt Howells的优秀答案加上其他人开始(参见下面代码中的链接)。 改进:
- 支持32位和64位。
- 修复@Matt Howells的答案中的一些问题:
-
extendedInfoPtr
的小内存泄漏 - “Win32”编译错误,和
- 我在调用
CreateJobObject
(使用Windows 10,Visual StudioCreateJobObject
位)时遇到了一个堆栈不平衡异常。
-
- 命名工作,所以如果你使用SysInternals,例如,你可以很容易地找到它。
- 有一个更简单的API和更少的代码。
以下是如何使用此代码:
// Get a Process object somehow. Process process = Process.Start(exePath, args); // Add the Process to ChildProcessTracker. ChildProcessTracker.AddProcess(process);
要支持Windows 7,需要:
- @adam smith描述了一个简单的app.manifest变化。
- 如果使用Visual Studio ,则需要添加注册表设置 。
在我的情况下,我不需要支持Windows 7,所以我在下面的静态构造函数的顶部有一个简单的检查。
/// <summary> /// Allows processes to be automatically killed if this parent process unexpectedly quits. /// This feature requires Windows 8 or greater. On Windows 7, nothing is done.</summary> /// <remarks>References: /// https://stackoverflow.com/a/4657392/386091 /// https://stackoverflow.com/a/9164742/386091 </remarks> public static class ChildProcessTracker { /// <summary> /// Add the process to be tracked. If our current process is killed, the child processes /// that we are tracking will be automatically killed, too. If the child process terminates /// first, that's fine, too.</summary> /// <param name="process"></param> public static void AddProcess(Process process) { if (s_jobHandle != IntPtr.Zero) { bool success = AssignProcessToJobObject(s_jobHandle, process.Handle); if (!success) throw new Win32Exception(); } } static ChildProcessTracker() { // This feature requires Windows 8 or later. To support Windows 7 requires // registry settings to be added if you are using Visual Studio plus an // app.manifest change. // https://stackoverflow.com/a/4232259/386091 // https://stackoverflow.com/a/9507862/386091 if (Environment.OSVersion.Version < new Version(6, 2)) return; // The job name is optional (and can be null) but it helps with diagnostics. // If it's not null, it has to be unique. Use SysInternals' Handle command-line // utility: handle -a ChildProcessTracker string jobName = "ChildProcessTracker" + Process.GetCurrentProcess().Id; s_jobHandle = CreateJobObject(IntPtr.Zero, jobName); var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION(); // This is the key flag. When our process is killed, Windows will automatically // close the job handle, and when that happens, we want the child processes to // be killed, too. info.LimitFlags = JOBOBJECTLIMIT.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION(); extendedInfo.BasicLimitInformation = info; int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length); try { Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); if (!SetInformationJobObject(s_jobHandle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length)) { throw new Win32Exception(); } } finally { Marshal.FreeHGlobal(extendedInfoPtr); } } [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string name); [DllImport("kernel32.dll")] static extern bool SetInformationJobObject(IntPtr job, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength); [DllImport("kernel32.dll", SetLastError = true)] static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process); // Windows will automatically close any open job handles when our process terminates. // This can be verified by using SysInternals' Handle utility. When the job handle // is closed, the child processes will be killed. private static readonly IntPtr s_jobHandle; } public enum JobObjectInfoType { AssociateCompletionPortInformation = 7, BasicLimitInformation = 2, BasicUIRestrictions = 4, EndOfJobTimeInformation = 6, ExtendedLimitInformation = 9, SecurityLimitInformation = 5, GroupInformation = 11 } [StructLayout(LayoutKind.Sequential)] public struct JOBOBJECT_BASIC_LIMIT_INFORMATION { public Int64 PerProcessUserTimeLimit; public Int64 PerJobUserTimeLimit; public JOBOBJECTLIMIT LimitFlags; public UIntPtr MinimumWorkingSetSize; public UIntPtr MaximumWorkingSetSize; public UInt32 ActiveProcessLimit; public Int64 Affinity; public UInt32 PriorityClass; public UInt32 SchedulingClass; } [Flags] public enum JOBOBJECTLIMIT : uint { JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000 } [StructLayout(LayoutKind.Sequential)] public struct IO_COUNTERS { public UInt64 ReadOperationCount; public UInt64 WriteOperationCount; public UInt64 OtherOperationCount; public UInt64 ReadTransferCount; public UInt64 WriteTransferCount; public UInt64 OtherTransferCount; } [StructLayout(LayoutKind.Sequential)] public struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION { public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; public IO_COUNTERS IoInfo; public UIntPtr ProcessMemoryLimit; public UIntPtr JobMemoryLimit; public UIntPtr PeakProcessMemoryUsed; public UIntPtr PeakJobMemoryUsed; }
我仔细测试了32位和64位版本的结构,通过编程比较托管和本地版本(总体大小以及每个成员的偏移量)。
我已经在Windows 7,8和10上测试了这个代码。
当你控制子进程运行的代码时,有一个替代方案可能适用于某些方法。 这种方法的好处是它不需要任何本地Windows调用。
基本的想法是将孩子的标准输入重定向到另一端连接到父母的流,并使用该流来检测父母何时离开。 当您使用System.Diagnostics.Process
启动孩子时,很容易确保其标准输入被重定向:
Process childProcess = new Process(); childProcess.StartInfo = new ProcessStartInfo("pathToConsoleModeApp.exe"); childProcess.StartInfo.RedirectStandardInput = true; childProcess.StartInfo.CreateNoWindow = true; // no sense showing an empty black console window which the user can't input into
然后,在子进程中,利用标准输入流中的Read
将总是返回至少1个字节的事实,直到流被关闭,当它们将开始返回0字节时。 下面是我最终这样做的概要。 我的方式也使用消息泵来保持除了看标准之外的其他东西的主线程,但是这种通用的方法也可以在没有消息泵的情况下使用。
using System; using System.IO; using System.Threading; using System.Windows.Forms; static int Main() { Application.Run(new MyApplicationContext()); return 0; } public class MyApplicationContext : ApplicationContext { private SynchronizationContext _mainThreadMessageQueue = null; private Stream _stdInput; public MyApplicationContext() { _stdInput = Console.OpenStandardInput(); // feel free to use a better way to post to the message loop from here if you know one ;) System.Windows.Forms.Timer handoffToMessageLoopTimer = new System.Windows.Forms.Timer(); handoffToMessageLoopTimer.Interval = 1; handoffToMessageLoopTimer.Tick += new EventHandler((obj, eArgs) => { PostMessageLoopInitialization(handoffToMessageLoopTimer); }); handoffToMessageLoopTimer.Start(); } private void PostMessageLoopInitialization(System.Windows.Forms.Timer t) { if (_mainThreadMessageQueue == null) { t.Stop(); _mainThreadMessageQueue = SynchronizationContext.Current; } // constantly monitor standard input on a background thread that will // signal the main thread when stuff happens. BeginMonitoringStdIn(null); // start up your application's real work here } private void BeginMonitoringStdIn(object state) { if (SynchronizationContext.Current == _mainThreadMessageQueue) { // we're already running on the main thread - proceed. var buffer = new byte[128]; _stdInput.BeginRead(buffer, 0, buffer.Length, (asyncResult) => { int amtRead = _stdInput.EndRead(asyncResult); if (amtRead == 0) { _mainThreadMessageQueue.Post(new SendOrPostCallback(ApplicationTeardown), null); } else { BeginMonitoringStdIn(null); } }, null); } else { // not invoked from the main thread - dispatch another call to this method on the main thread and return _mainThreadMessageQueue.Post(new SendOrPostCallback(BeginMonitoringStdIn), null); } } private void ApplicationTeardown(object state) { // tear down your application gracefully here _stdInput.Close(); this.ExitThread(); } }
注意这个方法:
-
实际启动的子.exe文件必须是一个控制台应用程序,因此它仍然附加到stdin / out / err。 就像在上面的例子中,我通过创建引用现有项目的小型控制台项目,实例化我的应用程序上下文并调用
Application.Run()
,轻松地调整了使用消息泵的现有应用程序(但未显示GUIApplication.Run()
在控制台.exe的Main
方法里面。 -
从技术上来说,这只是在父进程退出时发信号通知子进程,所以无论父进程是正常退出还是崩溃,它都将工作,但是子进程仍然执行自己的关闭。 这可能是也可能不是你想要的…
一种方法是将父进程的PID传递给孩子。 如果具有指定pid的进程存在或不存在,孩子将定期轮询。 如果没有,它只会退出。
您还可以在父级进程结束时通知子方法中的Process.WaitForExit方法,但在任务管理器中可能无法正常工作。
还有另一种相关的方法,简单而有效,在程序终止时完成子进程。 您可以从父级实现并附加一个调试器给他们; 当父进程结束时,子进程将被操作系统终止。 它可以通过两种方式将调试器连接到父级(注意,一次只能连接一个调试器)。 你可以在这里找到关于这个主题的更多信息。
在这里你有一个实用工具类,启动一个新的过程,并附加一个调试器。 Roger Knapp从这篇文章中改编了它。 唯一的要求是两个进程需要共享相同的位数。 您无法从64位进程调试32位进程,反之亦然。
public class ProcessRunner { #region "API imports" private const int DBG_CONTINUE = 0x00010002; private const int DBG_EXCEPTION_NOT_HANDLED = unchecked((int) 0x80010001); private enum DebugEventType : int { CREATE_PROCESS_DEBUG_EVENT = 3, //Reports a create-process debugging event. The value of u.CreateProcessInfo specifies a CREATE_PROCESS_DEBUG_INFO structure. CREATE_THREAD_DEBUG_EVENT = 2, //Reports a create-thread debugging event. The value of u.CreateThread specifies a CREATE_THREAD_DEBUG_INFO structure. EXCEPTION_DEBUG_EVENT = 1, //Reports an exception debugging event. The value of u.Exception specifies an EXCEPTION_DEBUG_INFO structure. EXIT_PROCESS_DEBUG_EVENT = 5, //Reports an exit-process debugging event. The value of u.ExitProcess specifies an EXIT_PROCESS_DEBUG_INFO structure. EXIT_THREAD_DEBUG_EVENT = 4, //Reports an exit-thread debugging event. The value of u.ExitThread specifies an EXIT_THREAD_DEBUG_INFO structure. LOAD_DLL_DEBUG_EVENT = 6, //Reports a load-dynamic-link-library (DLL) debugging event. The value of u.LoadDll specifies a LOAD_DLL_DEBUG_INFO structure. OUTPUT_DEBUG_STRING_EVENT = 8, //Reports an output-debugging-string debugging event. The value of u.DebugString specifies an OUTPUT_DEBUG_STRING_INFO structure. RIP_EVENT = 9, //Reports a RIP-debugging event (system debugging error). The value of u.RipInfo specifies a RIP_INFO structure. UNLOAD_DLL_DEBUG_EVENT = 7, //Reports an unload-DLL debugging event. The value of u.UnloadDll specifies an UNLOAD_DLL_DEBUG_INFO structure. } [StructLayout(LayoutKind.Sequential)] private struct DEBUG_EVENT { [MarshalAs(UnmanagedType.I4)] public DebugEventType dwDebugEventCode; public int dwProcessId; public int dwThreadId; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)] public byte[] bytes; } [DllImport("Kernel32.dll", SetLastError = true)] private static extern bool DebugActiveProcess(int dwProcessId); [DllImport("Kernel32.dll", SetLastError = true)] private static extern bool WaitForDebugEvent([Out] out DEBUG_EVENT lpDebugEvent, int dwMilliseconds); [DllImport("Kernel32.dll", SetLastError = true)] private static extern bool ContinueDebugEvent(int dwProcessId, int dwThreadId, int dwContinueStatus); [DllImport("Kernel32.dll", SetLastError = true)] public static extern bool IsDebuggerPresent(); #endregion public Process ChildProcess { get; set; } public bool StartProcess(string fileName) { var processStartInfo = new ProcessStartInfo(fileName) { UseShellExecute = false, WindowStyle = ProcessWindowStyle.Normal, ErrorDialog = false }; this.ChildProcess = Process.Start(processStartInfo); if (ChildProcess == null) return false; new Thread(NullDebugger) {IsBackground = true}.Start(ChildProcess.Id); return true; } private void NullDebugger(object arg) { // Attach to the process we provided the thread as an argument if (DebugActiveProcess((int) arg)) { var debugEvent = new DEBUG_EVENT {bytes = new byte[1024]}; while (!this.ChildProcess.HasExited) { if (WaitForDebugEvent(out debugEvent, 1000)) { // return DBG_CONTINUE for all events but the exception type var continueFlag = DBG_CONTINUE; if (debugEvent.dwDebugEventCode == DebugEventType.EXCEPTION_DEBUG_EVENT) continueFlag = DBG_EXCEPTION_NOT_HANDLED; ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, continueFlag); } } } else { //we were not able to attach the debugger //do the processes have the same bitness? //throw ApplicationException("Unable to attach debugger") // Kill child? // Send Event? // Ignore? } } }
用法:
new ProcessRunner().StartProcess("c:\\Windows\\system32\\calc.exe");
我看到两个选项:
- 如果您确切知道哪个子进程可以启动,并且您确定只能从您的主进程启动,那么您可以考虑简单地按名称搜索并杀死它们。
- 遍历所有进程,并杀死每一个有你的过程作为父母的进程(我想你需要先杀死子进程)。 这里解释了如何获得父进程ID。
我已经做了一个子进程管理库,其中父进程和子进程由于双向WCF管道而受到监视。 如果子进程终止或者父进程终止,则通知对方。 还有一个可用的调试器助手,可以将VS调试器自动附加到启动的子进程
项目地点:
http://www.crawler-lib.net/child-processes
NuGet软件包:
https://www.nuget.org/packages/ChildProcesses https://www.nuget.org/packages/ChildProcesses.VisualStudioDebug/
调用job.AddProcess过程开始后要做的更好:
prc.Start(); job.AddProcess(prc.Handle);
在终止之前调用AddProcess时,子进程不会被终止。 (Windows 7 SP1)
private void KillProcess(Process proc) { var job = new Job(); job.AddProcess(proc.Handle); job.Close(); }
我正在寻找这个问题的解决方案,不需要非托管代码。 我也无法使用标准的输入/输出重定向,因为它是一个Windows窗体应用程序。
我的解决方案是在父进程中创建一个命名管道,然后将子进程连接到相同的管道。 如果父进程退出,那么管道就会断开,孩子可以检测到。
以下是使用两个控制台应用程序的示例:
亲
private const string PipeName = "471450d6-70db-49dc-94af-09d3f3eba529"; public static void Main(string[] args) { Console.WriteLine("Main program running"); using (NamedPipeServerStream pipe = new NamedPipeServerStream(PipeName, PipeDirection.Out)) { Process.Start("child.exe"); Console.WriteLine("Press any key to exit"); Console.ReadKey(); } }
儿童
private const string PipeName = "471450d6-70db-49dc-94af-09d3f3eba529"; // same as parent public static void Main(string[] args) { Console.WriteLine("Child process running"); using (NamedPipeClientStream pipe = new NamedPipeClientStream(".", PipeName, PipeDirection.In)) { pipe.Connect(); pipe.BeginRead(new byte[1], 0, 1, PipeBrokenCallback, pipe); Console.WriteLine("Press any key to exit"); Console.ReadKey(); } } private static void PipeBrokenCallback(IAsyncResult ar) { // the pipe was closed (parent process died), so exit the child process too try { NamedPipeClientStream pipe = (NamedPipeClientStream)ar.AsyncState; pipe.EndRead(ar); } catch (IOException) { } Environment.Exit(1); }
使用事件处理程序在几个退出场景上进行挂接:
var process = Process.Start("program.exe"); AppDomain.CurrentDomain.DomainUnload += (s, e) => { process.Kill(); process.WaitForExit(); }; AppDomain.CurrentDomain.ProcessExit += (s, e) => { process.Kill(); process.WaitForExit(); }; AppDomain.CurrentDomain.UnhandledException += (s, e) => { process.Kill(); process.WaitForExit(); };