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

WinForms程序防重复运行:检测到重复时关闭已有实例

解决方案:检测重复运行并关闭已有WinForms实例

先修正Mutex的使用

你的Mutex代码存在潜在问题:未确保Mutex的释放,若程序异常退出可能导致Mutex残留,后续启动会误判为已有实例。建议补充释放逻辑:

private static Mutex _mutex;
private bool IsAlreadyRunning()
{
    const string appName = "RocketAlert";
    bool createdNew;
    _mutex = new Mutex(true, appName, out createdNew);
    // 绑定进程退出事件,确保Mutex释放
    AppDomain.CurrentDomain.ProcessExit += (s, e) => _mutex?.ReleaseMutex();
    return !createdNew;
}

方案一:改进命名管道实现(稳定可靠)

之前使用NamedPipeClientStream失败,大概率是已有实例未正确启动管道监听,或客户端连接逻辑有误。以下是完整可运行的实现:

1. 已有实例启动管道监听

在Program.cs的Main方法中,新实例启动后开启后台线程监听管道,接收关闭指令:

private static void StartPipeListener()
{
    const string pipeName = "RocketAlert_Pipe";
    Task.Run(async () =>
    {
        while (true)
        {
            try
            {
                using (var pipeServer = new NamedPipeServerStream(pipeName, PipeDirection.In))
                {
                    pipeServer.WaitForConnection();
                    // 读取任意字节作为关闭信号
                    var buffer = new byte[1];
                    pipeServer.Read(buffer, 0, 1);
                    // 退出程序
                    Application.Exit();
                    break;
                }
            }
            catch
            {
                // 忽略管道异常,继续监听
            }
        }
    });
}

在Main方法中整合逻辑:

[STAThread]
static void Main()
{
    Application.SetHighDpiMode(HighDpiMode.SystemAware);
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    if (IsAlreadyRunning())
    {
        SendCloseCommandToExistingInstance();
        return;
    }

    StartPipeListener();
    Application.Run(new YourMainForm()); // 替换为你的主窗体(即使静默隐藏)
}

2. 新实例发送关闭指令

实现发送指令的方法:

private static void SendCloseCommandToExistingInstance()
{
    const string pipeName = "RocketAlert_Pipe";
    try
    {
        using (var pipeClient = new NamedPipeClientStream(".", pipeName, PipeDirection.Out))
        {
            pipeClient.Connect(1000); // 1秒超时
            pipeClient.WriteByte(1); // 发送关闭信号
        }
    }
    catch
    {
        // 连接失败,说明已有实例可能已退出,可直接启动新实例
    }
}

方案二:使用Windows消息(更轻量)

WinForms程序即使后台静默,主窗体仍有窗口句柄,可通过窗口标题/类名找到已有实例,发送WM_CLOSE消息关闭:

1. 定义Win32 API调用

在Program.cs中添加:

[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport("user32.dll", SetLastError = true)]
private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

private const uint WM_CLOSE = 0x0010;

2. 修改重复运行检测后的逻辑

[STAThread]
static void Main()
{
    // 初始化代码...

    if (IsAlreadyRunning())
    {
        // 替换为你的主窗体标题,或用窗体类名(命名空间+类名)
        IntPtr existingWindow = FindWindow(null, "RocketAlertMainForm");
        if (existingWindow != IntPtr.Zero)
        {
            PostMessage(existingWindow, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
        }
        // 等待已有实例退出,再启动新实例
        Thread.Sleep(500);
    }

    Application.Run(new YourMainForm());
}

选择建议

  • 若需后续扩展进程间通信(如传递配置数据),优先选命名管道方案;
  • 仅需简单关闭已有实例,Windows消息方案代码更简洁、开销更低。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 10:42:47