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

Windows服务安装后重复启动SysDemConnect实例的解决问询

SysDemConnect Windows服务重复启动实例问题

背景

  • 主应用为SysDemConnect,用户通过桌面快捷方式启动
  • 应用内置「安装Windows服务」按钮,该服务用于系统重启时自动启动SysDemConnect,应对断电/意外关机场景
  • 服务设置为安装后自动启动,此为预期必需行为

当前问题

安装服务后,服务启动的同时会额外启动一个SysDemConnect实例,但此时用户已经打开了该应用,不符合「已有实例运行时不启动新实例」的预期。

初始OnStart代码

protected override void OnStart(string[] args)
{
    try
    {
        Process cmd = new Process();
        string filename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SysDem.Connect.exe");
        cmd.StartInfo.FileName = filename;
        cmd.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
        cmd.StartInfo.Arguments = null;
        cmd.Start();
        processId = cmd.Id;
        sessionID = cmd.SessionId;
    }
    catch (Exception ex)
    {
        ConnectServiceLogger.WriteToConnectServiceLog(ex.Message);
    }
}

尝试过的方案及问题

修改后的代码尝试检测进程是否存在,但系统重启时OnStart()方法看似未执行(服务仍处于运行状态):

protected override void OnStart(string[] args)
{
    try
    {
        Process[] sysdemConnectProcess = Process.GetProcessesByName("SysDem.Connect");
        bool isRunning = sysdemConnectProcess.Length > 0;

        if (!isRunning)
        {
            Process cmd = new Process();
            string filename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SysDem.Connect.exe");
            cmd.StartInfo.FileName = filename;
            cmd.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
            cmd.StartInfo.Arguments = null;
            cmd.Start();
            processId = cmd.Id;
            sessionID = cmd.SessionId;
        }
    }
    catch (Exception ex)
    {
        ConnectServiceLogger.WriteToConnectServiceLog(ex.Message);
    }
}

解决思路

1. 修正进程检测的会话隔离问题

Windows服务默认运行在Session 0,而用户桌面启动的应用在用户会话(如Session 1),Vista及以后的系统存在Session 0隔离机制,导致服务无法直接读取用户会话的GUI进程信息。原代码用Process.GetProcessesByName可能无法获取到用户会话中的实例,导致错误启动新进程。

可以通过WMI查询所有会话的进程来解决:

using System.Management;

private bool IsAppRunning()
{
    var processQuery = new SelectQuery("SELECT * FROM Win32_Process WHERE Name = 'SysDem.Connect.exe'");
    using (var searcher = new ManagementObjectSearcher(processQuery))
    {
        foreach (var processObj in searcher.Get())
        {
            // 只要存在该进程,无论哪个会话都返回true
            return true;
            // 若仅需判断用户会话实例,可添加:
            // int sessionId = Convert.ToInt32(processObj["SessionId"]);
            // if (sessionId != 0) return true;
        }
    }
    return false;
}

注意:需添加System.Management程序集引用,且服务需具备读取WMI的权限。

2. 用全局互斥锁实现单实例(更可靠)

在SysDemConnect主程序入口添加全局互斥锁,不管是服务启动还是用户启动,都能检测到已有实例并自动退出,从根源避免重复实例:

static void Main()
{
    // 全局命名空间的互斥锁,跨会话生效
    using (var instanceMutex = new Mutex(true, "Global\\SysDemConnect_SingleInstance", out bool isNewInstance))
    {
        if (isNewInstance)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }
        else
        {
            // 已有实例运行,可弹窗提示或直接退出
            MessageBox.Show("SysDemConnect已在运行");
            return;
        }
    }
}

这种方式无需在服务端做复杂检测,服务可保留原启动逻辑,新启动的进程会自行判断并退出。

3. 排查OnStart未执行的假象

修改后的代码重启时看似OnStart未执行,实际可能是:

  • 进程检测逻辑导致服务未启动进程,看起来像没执行:在try块开头添加日志,确认OnStart是否触发:ConnectServiceLogger.WriteToConnectServiceLog("OnStart 方法已触发");
  • 存在未捕获的异常:检查日志是否记录完整错误,比如WMI操作的异常是否被正确捕获
  • 进程名称匹配错误:GetProcessesByName的参数是进程的友好名称(不带.exe),可通过任务管理器确认进程的实际名称,或改用路径匹配:Process.GetProcesses().Any(p => p.MainModule?.FileName.EndsWith("SysDem.Connect.exe") == true)

4. 服务启动参数区分

服务启动SysDemConnect时传递专属参数(如-service),应用程序收到该参数时,若检测到已有实例则直接退出,不弹出UI:

// 服务端OnStart修改启动参数
cmd.StartInfo.Arguments = "-service";

// 应用程序Main方法处理参数
static void Main(string[] args)
{
    bool isServiceLaunch = args.Contains("-service");
    using (var mutex = new Mutex(true, "Global\\SysDemConnectInstanceMutex", out bool createdNew))
    {
        if (createdNew)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }
        else
        {
            if (isServiceLaunch)
            {
                // 服务启动的实例直接退出,不提示
                return;
            }
            MessageBox.Show("SysDemConnect已在运行");
        }
    }
}

内容的提问来源于stack exchange,提问作者Lech Krawczyk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 22:14:50