编辑
2025-11-28
C#
00

目录

🎯 核心问题分析
🚀 方法一:Process类基础监控(推荐新手)
⚡ 方法二:事件驱动监控(性能优化)
🕐 方法三:定时器轮询监控(灵活控制)
🪟 方法五:窗口句柄监控(GUI应用专用)
💡 黄金实践建议
🎯 选择策略指南
⚠️ 常见坑点提醒
🚀 性能优化技巧
🎊 总结

你是否遇到过这样的开发场景:需要在某个应用程序关闭后自动执行清理操作?或者开发自动化测试工具时,需要等待被测应用退出后生成测试报告?又或者在开发插件管理器时,需要在主程序关闭后清理临时文件?

这些看似复杂的需求,其实都指向一个核心问题:如何在C#中准确检测其他应用程序是否已经关闭。本文将为你提供6种实战级解决方案,从基础到高级,让你轻松应对各种监控场景。

🎯 核心问题分析

在实际开发中,我们经常需要监控其他应用程序的状态,主要痛点包括:

  • 时效性要求:需要第一时间感知应用关闭事件
  • 资源消耗:频繁轮询会造成性能开销
  • 权限限制:某些监控方法需要特殊权限
  • 多实例处理:同名应用的多个实例如何区分

针对这些问题,我们需要根据不同场景选择合适的监控策略。

🚀 方法一:Process类基础监控(推荐新手)

最直接的方法是使用.NET内置的System.Diagnostics.Process类:

C#
using System.Diagnostics; namespace AppProcessMonitor { internal class Program { static void Main(string[] args) { // 要监控的应用程序名称(不含.exe扩展名) string processName = "notepad"; Console.WriteLine($"开始监控 {processName} 进程..."); // 首先检查进程是否正在运行 if (!IsProcessRunning(processName)) { Console.WriteLine($"{processName} 进程未运行!"); return; } // 持续监控进程状态直到关闭 while (IsProcessRunning(processName)) { Console.WriteLine($"{processName} 正在运行中..."); Thread.Sleep(1000); // 每秒检查一次 } Console.WriteLine($"🎉 {processName} 已关闭!"); // 在这里执行应用关闭后的操作 } /// <summary> /// 检查指定名称的进程是否正在运行 /// </summary> static bool IsProcessRunning(string processName) { Process[] processes = Process.GetProcessesByName(processName); return processes.Length > 0; } } }

image.png

适用场景:简单的进程存在性检查,开发调试阶段

优点:代码简单,易于理解

注意事项:频繁轮询可能影响性能

⚡ 方法二:事件驱动监控(性能优化)

使用Process对象的事件机制,避免轮询带来的性能开销:

C#
using System; using System.Diagnostics; class EventDrivenMonitor { static void Main(string[] args) { int processId = GetTargetProcessId("notepad"); if (processId == -1) { Console.WriteLine("目标进程未找到!"); return; } try { Process process = Process.GetProcessById(processId); // 🔥 关键:启用事件触发 process.EnableRaisingEvents = true; // 注册进程退出事件 process.Exited += (sender, e) => { Console.WriteLine($"🎯 检测到进程退出! 退出代码: {process.ExitCode}"); // 在这里执行关闭后的业务逻辑 ExecuteCleanupTasks(); }; Console.WriteLine($"正在监控进程ID: {processId}"); process.WaitForExit(); // 阻塞等待进程退出 } catch (ArgumentException) { Console.WriteLine("进程不存在或已关闭"); } } static int GetTargetProcessId(string processName) { Process[] processes = Process.GetProcessesByName(processName); return processes.Length > 0 ? processes[0].Id : -1; } static void ExecuteCleanupTasks() { Console.WriteLine("执行清理任务..."); // 添加你的清理代码 } }

image.png

适用场景:需要精确时间点响应的监控需求

优点:零延迟检测,无性能开销

黄金实践:记得设置EnableRaisingEvents = true

🕐 方法三:定时器轮询监控(灵活控制)

适合需要周期性检查状态并执行其他任务的场景:

C#
using System; using System.Diagnostics; using System.Timers; class TimerBasedMonitor { private static Timer _monitorTimer; private static string _targetProcessName; private static bool _wasRunning = false; static void Main(string[] args) { _targetProcessName = "notepad"; StartMonitoring(TimeSpan.FromSeconds(2)); Console.WriteLine("监控已启动,按Enter键停止..."); Console.ReadLine(); StopMonitoring(); } static void StartMonitoring(TimeSpan interval) { _wasRunning = IsProcessRunning(_targetProcessName); _monitorTimer = new Timer(interval.TotalMilliseconds); _monitorTimer.Elapsed += OnTimerElapsed; _monitorTimer.AutoReset = true; _monitorTimer.Start(); } static void OnTimerElapsed(object sender, ElapsedEventArgs e) { bool isRunning = IsProcessRunning(_targetProcessName); // 🎯 状态变化检测:从运行到停止 if (_wasRunning && !isRunning) { Console.WriteLine($"🚨 {_targetProcessName} 已关闭!"); OnProcessClosed(); } // 从停止到运行 else if (!_wasRunning && isRunning) { Console.WriteLine($"▶️ {_targetProcessName} 已启动!"); } _wasRunning = isRunning; } static void OnProcessClosed() { // 在这里添加进程关闭后的处理逻辑 Console.WriteLine("执行应用关闭后的操作..."); } static bool IsProcessRunning(string processName) { return Process.GetProcessesByName(processName).Length > 0; } static void StopMonitoring() { _monitorTimer?.Stop(); _monitorTimer?.Dispose(); } }

image.png

适用场景:需要同时监控多个应用或执行周期性任务

性能建议:监控间隔建议设置在500ms-2s之间,平衡及时性和性能

🪟 方法五:窗口句柄监控(GUI应用专用)

专门针对有界面的应用程序进行监控:

C#
using System.Diagnostics; using System.Runtime.InteropServices; using System.Timers; using Timer = System.Timers.Timer; namespace AppProcessMonitor { internal class Program { [DllImport("user32.dll")] static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll")] static extern bool IsWindow(IntPtr hWnd); [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId); static void Main(string[] args) { string windowTitle = "notepad"; IntPtr windowHandle = FindWindowByTitle(windowTitle); if (windowHandle == IntPtr.Zero) { Console.WriteLine($"未找到包含 '{windowTitle}' 的窗口"); return; } GetWindowThreadProcessId(windowHandle, out uint processId); Console.WriteLine($"找到窗口,进程ID: {processId}"); // 持续监控窗口是否存在 while (IsWindow(windowHandle)) { Thread.Sleep(500); } Console.WriteLine("🎊 窗口已关闭!"); HandleWindowClosed(); } static IntPtr FindWindowByTitle(string partialTitle) { Process[] processes = Process.GetProcesses(); foreach (Process process in processes) { if (!string.IsNullOrEmpty(process.MainWindowTitle) && process.MainWindowTitle.Contains(partialTitle)) { return process.MainWindowHandle; } } return IntPtr.Zero; } static void HandleWindowClosed() { Console.WriteLine("执行窗口关闭后的处理..."); } } }

适用场景:监控特定窗口应用,UI自动化测试

优势:能够监控特定窗口,支持窗口标题匹配

局限性:仅适用于有界面的应用程序

💡 黄金实践建议

🎯 选择策略指南

  1. 基础监控需求 → Process类 + 定时器轮询
  2. 高性能要求 → Process事件驱动监控
  3. 系统级监控 → WMI事件监听
  4. GUI应用监控 → Windows API窗口监控
  5. 精确控制需求 → 进程间通信方案

⚠️ 常见坑点提醒

C#
// ❌ 错误:忘记启用事件 Process process = Process.GetProcessById(id); process.Exited += OnProcessExited; // 不会触发! // ✅ 正确:必须启用事件 process.EnableRaisingEvents = true; process.Exited += OnProcessExited; // ❌ 错误:资源泄漏 while (true) { Process.GetProcessesByName("app"); // 每次都创建新对象 } // ✅ 正确:及时释放资源 using (var timer = new Timer()) { // 监控逻辑 } // 自动释放

🚀 性能优化技巧

  1. 合理设置监控间隔:500ms-2s平衡性能与实时性
  2. 使用事件而非轮询:Process.Exited事件零延迟
  3. 及时释放资源:避免句柄泄漏
  4. 批量处理:一次获取多个进程信息

🎊 总结

本文介绍了6种C#监控应用程序关闭的方法,从简单的Process类轮询企业级的WMI监控,再到高精度的进程间通信。每种方法都有其适用场景:

  • Process类监控:入门首选,简单易用
  • 事件驱动监控:性能最优,零延迟响应
  • WMI系统监控:功能强大,适合企业级应用

选择合适的监控策略,能让你的C#应用更加智能和可靠。记住,没有最好的方法,只有最适合的方案


💬 技术交流时间

  1. 你在项目中是如何处理进程监控的?遇到过哪些坑?
  2. 对于高并发场景下的进程监控,你有什么优化建议?

觉得文章有用的话,别忘了转发给更多C#开发同行! 🚀

#C#开发 #进程监控 #编程技巧 #系统编程

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!