如何在.NET Core Web应用中启停并监控常驻外部EXE程序?
实现.NET Core Web应用管理常驻EXE的方案
核心思路
用System.Diagnostics.Process管理目标EXE进程,结合.NET Core的IHostApplicationLifetime生命周期事件,确保Web应用启动时自动拉起EXE、终止时自动停止EXE;再通过API暴露进程状态查询和操作接口,前端UI调用这些接口实现交互。
步骤分解
1. 封装进程管理单例服务
创建全局唯一的进程管理服务,统一控制EXE的启停状态:
public class ExeProcessManager { private Process _exeProcess; private readonly string _exePath; private readonly ILogger<ExeProcessManager> _logger; public ExeProcessManager(IConfiguration config, ILogger<ExeProcessManager> logger) { _exePath = config["ExeSettings:TargetPath"]; // 从配置文件读取EXE路径 _logger = logger; } public bool IsRunning => _exeProcess?.HasExited == false; public void Start() { if (IsRunning) return; _exeProcess = new Process { StartInfo = new ProcessStartInfo(_exePath) { UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true }, EnableRaisingEvents = true }; // 监听进程意外退出事件 _exeProcess.Exited += (sender, e) => { _logger.LogWarning("目标EXE意外退出,退出码:{Code}", _exeProcess.ExitCode); _exeProcess.Dispose(); _exeProcess = null; }; // 捕获EXE输出和错误日志 _exeProcess.OutputDataReceived += (sender, e) => { if (!string.IsNullOrEmpty(e.Data)) _logger.LogInformation("EXE输出:{Content}", e.Data); }; _exeProcess.ErrorDataReceived += (sender, e) => { if (!string.IsNullOrEmpty(e.Data)) _logger.LogError("EXE错误:{Content}", e.Data); }; _exeProcess.Start(); _exeProcess.BeginOutputReadLine(); _exeProcess.BeginErrorReadLine(); _logger.LogInformation("已启动目标EXE"); } public void Stop() { if (!IsRunning) return; try { _exeProcess.Kill(true); // 终止进程及子进程 _exeProcess.WaitForExit(5000); // 等待5秒确保完全退出 _logger.LogInformation("已停止目标EXE"); } catch (Exception ex) { _logger.LogError(ex, "停止EXE时发生错误"); } finally { _exeProcess?.Dispose(); _exeProcess = null; } } public void Restart() { Stop(); Thread.Sleep(1000); // 短暂延迟避免进程未完全退出就重启 Start(); } }
2. 集成Web应用生命周期
在Program.cs中注册服务,并绑定应用启动/停止事件:
var builder = WebApplication.CreateBuilder(args); // 注册进程管理服务为单例 builder.Services.AddSingleton<ExeProcessManager>(); var app = builder.Build(); // 获取生命周期服务和进程管理器实例 var lifetime = app.Services.GetRequiredService<IHostApplicationLifetime>(); var processManager = app.Services.GetRequiredService<ExeProcessManager>(); // 应用启动完成后自动启动EXE lifetime.ApplicationStarted.Register(() => { processManager.Start(); }); // 应用关闭前自动停止EXE lifetime.ApplicationStopping.Register(() => { processManager.Stop(); }); // 注册API控制器 app.MapControllers(); // 配置静态文件(用于托管UI页面) app.UseStaticFiles(); app.Run();
3. 暴露API接口供UI调用
创建控制器提供进程操作接口:
[ApiController] [Route("api/process")] public class ProcessController : ControllerBase { private readonly ExeProcessManager _processManager; public ProcessController(ExeProcessManager processManager) { _processManager = processManager; } [HttpGet("status")] public IActionResult GetStatus() { return Ok(new { IsRunning = _processManager.IsRunning }); } [HttpPost("start")] public IActionResult StartProcess() { _processManager.Start(); return Ok("已启动目标进程"); } [HttpPost("stop")] public IActionResult StopProcess() { _processManager.Stop(); return Ok("已停止目标进程"); } [HttpPost("restart")] public IActionResult RestartProcess() { _processManager.Restart(); return Ok("已重启目标进程"); } }
4. UI层实现交互
在wwwroot目录下创建静态页面,调用API实现状态展示和操作:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>EXE进程管理</title> <style> .status-running { color: green; } .status-stopped { color: red; } button { margin: 0 8px; padding: 6px 12px; } </style> </head> <body> <h2>目标EXE状态:<span id="status" class="status-stopped">已停止</span></h2> <div> <button id="startBtn">启动</button> <button id="stopBtn">停止</button> <button id="restartBtn">重启</button> </div> <script> // 每秒刷新一次状态 setInterval(updateStatus, 1000); async function updateStatus() { const res = await fetch('/api/process/status'); const data = await res.json(); const statusEl = document.getElementById('status'); if (data.IsRunning) { statusEl.textContent = "运行中"; statusEl.className = "status-running"; } else { statusEl.textContent = "已停止"; statusEl.className = "status-stopped"; } } // 绑定按钮事件 document.getElementById('startBtn').addEventListener('click', async () => { await fetch('/api/process/start', { method: 'POST' }); updateStatus(); }); document.getElementById('stopBtn').addEventListener('click', async () => { await fetch('/api/process/stop', { method: 'POST' }); updateStatus(); }); document.getElementById('restartBtn').addEventListener('click', async () => { await fetch('/api/process/restart', { method: 'POST' }); updateStatus(); }); // 页面加载时初始化状态 updateStatus(); </script> </body> </html>
注意事项
- 权限配置:确保Web应用的运行账户有足够权限启动目标EXE,涉及系统级程序时需提升权限。
- 异常重启:如果需要EXE意外退出后自动重启,可在
Exited事件中调用Start方法。 - 跨平台适配:部署到Linux/macOS时,需调整
ProcessStartInfo的配置(比如CreateNoWindow在Linux下无效)。 - 日志监控:建议将EXE的输出和错误日志持久化,方便排查问题。
内容的提问来源于stack exchange,提问作者Anthony
相关产品推荐
相关产品推荐

