C#实现进程退出时执行代码及跨进程关闭的问题
我来帮你解决这个问题!你需要让第一个应用在被第二个应用关闭时执行指定的退出代码,这里分几种场景来处理,因为不同的关闭方式会影响退出代码的执行逻辑:
解决方案:让第一个应用在退出时执行指定代码
1. 处理正常退出场景(主动关闭、外部发送关闭信号)
如果第二个应用是通过正常方式关闭第一个应用(比如关闭主窗口、发送退出信号),我们可以通过注册.NET的退出事件来捕获退出时机,执行你的DoSomethingOnExit方法。
修改第一个应用的代码:
using System; using System.Threading; public class First { public static void Main(string[] args) { // 注册进程退出事件:当进程正常终止时触发 AppDomain.CurrentDomain.ProcessExit += (sender, e) => { DoSomethingOnExit(); }; // 注册控制台关闭事件:处理用户关闭控制台窗口、Ctrl+C等场景 Console.CancelKeyPress += (sender, e) => { DoSomethingOnExit(); // 设置Cancel=true,让进程正常退出而不是立即终止 e.Cancel = true; }; Console.WriteLine("第一个应用已启动,按Enter退出或等待第二个应用关闭..."); Console.ReadLine(); } // 把方法改为static,方便在静态事件处理中调用;如果需要实例方法,也可以先实例化First再调用 public static void DoSomethingOnExit() { Console.WriteLine("It Works"); Thread.Sleep(1000); } }
第二个应用的关闭代码(正常关闭):
如果第一个应用是控制台程序,CloseMainWindow()可能无法直接触发退出(因为控制台程序在Console.ReadLine()时阻塞),所以可以尝试先发送信号,或者等待用户输入的逻辑结束。如果是带UI的应用,CloseMainWindow()会更有效:
using System.Diagnostics; public class Second { public void CloseFirstApp(int processId) { try { var targetProcess = Process.GetProcessById(processId); // 尝试正常关闭主窗口(适用于UI程序,控制台程序可能需要其他方式) if (targetProcess.CloseMainWindow()) { // 等待进程退出 targetProcess.WaitForExit(); Console.WriteLine("第一个应用已正常关闭,退出代码已执行"); } else { // 正常关闭失败,降级为强制终止(这种情况退出代码不会执行) targetProcess.Kill(); Console.WriteLine("第一个应用无法正常关闭,已强制终止"); } } catch (ArgumentException) { Console.WriteLine("未找到目标进程"); } } }
2. 处理强制终止场景(第二个应用用Kill()关闭第一个)
如果第二个应用必须用Process.Kill()强制终止第一个应用,那么.NET的退出事件(ProcessExit、CancelKeyPress)都不会触发,因为Kill()会立即终止进程,不给执行退出代码的机会。这种情况下,我们需要让两个应用之间建立通信,让第一个应用收到关闭通知后主动执行退出代码再退出。
用命名管道实现优雅关闭(推荐)
第一个应用代码:
using System; using System.IO.Pipes; using System.Threading; using System.Threading.Tasks; public class First { private static bool _shouldExit = false; public static void Main(string[] args) { // 启动后台任务监听关闭信号 _ = Task.Run(ListenForCloseSignal); Console.WriteLine("第一个应用已启动,等待关闭信号或按Enter退出..."); while (!_shouldExit) { // 检查用户是否按Enter退出 if (Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Enter) { _shouldExit = true; } Thread.Sleep(100); } // 退出前执行代码 DoSomethingOnExit(); } private static void ListenForCloseSignal() { // 创建命名管道服务器,监听来自第二个应用的信号 using var pipeServer = new NamedPipeServerStream("FirstAppClosePipe", PipeDirection.InOut); pipeServer.WaitForConnection(); // 收到关闭信号,标记退出 _shouldExit = true; } public static void DoSomethingOnExit() { Console.WriteLine("It Works"); Thread.Sleep(1000); } }
第二个应用代码:
using System.IO.Pipes; using System.Diagnostics; public class Second { public void GracefullyCloseFirstApp(int processId) { try { var targetProcess = Process.GetProcessById(processId); // 连接到第一个应用的命名管道,发送关闭信号 using var pipeClient = new NamedPipeClientStream(".", "FirstAppClosePipe", PipeDirection.InOut); pipeClient.Connect(5000); // 等待5秒连接超时 // 等待第一个应用执行退出代码并退出 targetProcess.WaitForExit(); Console.WriteLine("第一个应用已优雅关闭,退出代码已执行"); } catch (ArgumentException) { Console.WriteLine("未找到目标进程"); } catch (TimeoutException) { // 连接失败,强制终止 var targetProcess = Process.GetProcessById(processId); targetProcess.Kill(); Console.WriteLine("无法与第一个应用通信,已强制终止"); } } }
关键注意点:
- 如果必须用
Process.Kill(),没有办法让第一个应用执行退出代码,因为Kill是操作系统强制终止进程,进程没有机会执行任何代码。所以尽量使用优雅关闭的方式。 - 控制台程序的
CloseMainWindow()效果有限,因为控制台程序的消息循环和UI程序不同,所以用命名管道或其他IPC方式更可靠。
内容的提问来源于stack exchange,提问作者Homer Tw




