You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows服务停止时子进程被终止的C#监控程序问题求助

解决Windows服务停止时终止子进程的问题

嘿,我之前也踩过这个坑!当用NSSM把控制台监控程序转成Windows服务后,停止服务时子进程被连带终止,核心原因大多和**Windows作业对象(Job Object)**有关——NSSM默认会把服务进程加入一个作业,当服务停止时,系统会终止这个作业下的所有进程,包括你监控的子进程。下面给你几个靠谱的解决办法:

方法一:修改NSSM配置,禁用作业对象

这是最简单的方案,不用改代码:

  1. 如果已经安装了服务,先停止并卸载:
    nssm stop YourMonitorService
    nssm remove YourMonitorService confirm
    
  2. 重新安装服务,同时设置AppNoJob参数为1:
    nssm install YourMonitorService "C:\Path\To\Your\Monitor.exe"
    nssm set YourMonitorService AppNoJob 1
    
  3. 启动服务测试:
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:26:49