Windows服务停止时子进程被终止的C#监控程序问题求助
解决Windows服务停止时终止子进程的问题
嘿,我之前也踩过这个坑!当用NSSM把控制台监控程序转成Windows服务后,停止服务时子进程被连带终止,核心原因大多和**Windows作业对象(Job Object)**有关——NSSM默认会把服务进程加入一个作业,当服务停止时,系统会终止这个作业下的所有进程,包括你监控的子进程。下面给你几个靠谱的解决办法:
方法一:修改NSSM配置,禁用作业对象
这是最简单的方案,不用改代码:
- 如果已经安装了服务,先停止并卸载:
nssm stop YourMonitorService nssm remove YourMonitorService confirm - 重新安装服务,同时设置
AppNoJob参数为1:nssm install YourMonitorService "C:\Path\To\Your\Monitor.exe" nssm set YourMonitorService AppNoJob 1 - 启动服务测试:
nssm start YourMonitorService
这个参数会让NSSM不把服务进程纳入作业对象,停止服务时就不会牵连子进程了。
方法二:在C#代码里让子进程脱离父进程的作业
如果NSSM的配置不管用,或者你想从代码层面彻底解决,可以通过P/Invoke调用CreateProcess,指定CREATE_BREAKAWAY_FROM_JOB标志,让子进程脱离父进程的作业关联。
先写一个辅助类:
using System; using System.Diagnostics; using System.Runtime.InteropServices; public static class ProcessLauncher { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct STARTUPINFO { public int cb; public string lpReserved; public string lpDesktop; public string lpTitle; public int dwX; public int dwY; public int dwXSize; public int dwYSize; public int dwXCountChars; public int dwYCountChars; public int dwFillAttribute; public int dwFlags; public short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } [StructLayout(LayoutKind.Sequential)] private struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] private static extern bool CreateProcess( string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool CloseHandle(IntPtr hObject); private const uint CREATE_BREAKAWAY_FROM_JOB = 0x01000000; public static Process StartDetachedProcess(string exePath, string arguments = "") { var startupInfo = new STARTUPINFO(); startupInfo.cb = Marshal.SizeOf(startupInfo); PROCESS_INFORMATION processInfo; var fullCommand = $"{exePath} {arguments}"; if (!CreateProcess(null, fullCommand, IntPtr.Zero, IntPtr.Zero, false, CREATE_BREAKAWAY_FROM_JOB, IntPtr.Zero, null, ref startupInfo, out processInfo)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } // 关闭不需要的句柄,避免资源泄漏 CloseHandle(processInfo.hThread); CloseHandle(processInfo.hProcess); return Process.GetProcessById(processInfo.dwProcessId); } }
然后在你的监控程序里,用这个方法启动子进程:
// 替换成你的子进程实际路径 var childProcess = ProcessLauncher.StartDetachedProcess(@"C:\YourApps\ChildConsole.exe");
这样启动的子进程就不会和服务进程绑定到同一个作业,停止服务时它会继续运行。
方法三:确保服务停止逻辑不主动终止子进程
最后检查一下你的服务代码,在OnStop方法里,不要写主动杀掉子进程的逻辑——比如不要遍历监控的进程列表调用Kill(),只需要停止监控循环、释放资源即可。
举个正确的OnStop示例:
protected override void OnStop() { // 停止监控计时器或循环 _monitorTimer?.Stop(); _monitorTimer?.Dispose(); // 清理其他资源,但不要终止子进程 _isMonitoring = false; }
先从方法一开始试,不行再用方法二,基本就能解决问题了!
内容的提问来源于stack exchange,提问作者Karel Bém
相关产品推荐
相关产品推荐

