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

WPF应用后台运行时如何恢复MainWindow窗口?

WPF单实例应用:点击exe恢复后台窗口的解决方案

问题描述

我在WPF应用的App.xaml.cs中重写了启动函数,逻辑如下:

  • 启动时检查是否已有实例运行,存在则关闭第二个实例
  • 判断是否为开机自动启动,若是则隐藏MainWindow,并订阅窗口状态变化,当状态变为Normal时显示窗口

预期效果:开机时自动隐藏窗口,用户手动点击exe图标时显示窗口。目前开机自动启动逻辑正常,但应用在后台运行时,点击exe会弹出错误提示Another instance of the application is already running.,需要实现点击exe时直接恢复后台的MainWindow。

用户提供的原代码:

OnStartup重写代码

protected override void OnStartup(StartupEventArgs e)
{
    const string mutexName = "YourAppMutexName";

    _mutex = new Mutex(true, mutexName, out bool isNewInstance);

    if (isNewInstance)
    {
        // 已有应用实例在运行
        MessageBox.Show("Another instance of the application is already running.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
        Current.Shutdown();
    }
    else
    {
        base.OnStartup(e);

        bool startedAutomatically = IsStartedAutomatically(e.Args);


        NavigationStore navigationStore = new();

        navigationStore.CurrentVM = new LoginVM(navigationStore);

       
        if (startedAutomatically)
        {
            // 应用由Windows开机自动启动
            // 在此场景下执行所需操作(例如:隐藏主窗口)
            MainWindow = new MainWindow()
            {
                DataContext = new MainWindowVM(navigationStore)
            };
            MainWindow.Hide();
            MessageBox.Show("App running but hidden because of auto startup");
            MainWindow.StateChanged += (sender, args) =>
            {
                if (MainWindow.WindowState == WindowState.Normal)
                {
                    MainWindow.Show();
                }
            };
        }
        else
        {
            // 应用由用户手动启动
            // 在此场景下执行所需操作(例如:显示主窗口)
            MainWindow = new MainWindow()
            {
                DataContext = new MainWindowVM(navigationStore)
            };
            MainWindow.Show();
            MessageBox.Show("App running...");
        }

        AddToStartup();
    }
}

IsStartedAutomatically方法

private bool IsStartedAutomatically(string[] args)
{
    // 检查是否存在"--startup"命令行参数
    return Array.Exists(args, arg => arg.Equals("--startup", StringComparison.OrdinalIgnoreCase));
}

解决方案

核心修改点

  1. 修正Mutex逻辑:原代码的isNewInstance判断分支完全写反了,new Mutex(true, mutexName, out isNewInstance)中,isNewInstance为true表示当前是新实例(无其他实例运行),false表示已有实例在运行。
  2. 添加窗口激活逻辑:检测到已有实例时,不要直接弹窗关闭新实例,而是通过Windows API找到后台窗口,将其恢复并激活。

修改后的完整代码

首先在App.xaml.cs顶部添加Windows API导入:

using System.Runtime.InteropServices;

然后重写OnStartup方法:

private Mutex _mutex;
const string mutexName = "YourAppMutexName";

// Windows API声明
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

protected override void OnStartup(StartupEventArgs e)
{
    _mutex = new Mutex(true, mutexName, out bool isNewInstance);

    if (!isNewInstance)
    {
        // 已有实例运行,尝试激活后台窗口
        // 替换为你的MainWindow实际标题
        IntPtr mainWindowHandle = FindWindow(null, "MainWindowTitle"); 
        if (mainWindowHandle != IntPtr.Zero)
        {
            // 恢复窗口(处理最小化/隐藏状态)并前置激活
            ShowWindow(mainWindowHandle, 9); // SW_RESTORE = 9
            SetForegroundWindow(mainWindowHandle);
        }
        Current.Shutdown();
        return;
    }

    // 新实例启动逻辑
    base.OnStartup(e);

    bool startedAutomatically = IsStartedAutomatically(e.Args);

    NavigationStore navigationStore = new();
    navigationStore.CurrentVM = new LoginVM(navigationStore);

    // 统一实例化MainWindow,避免重复代码
    MainWindow = new MainWindow()
    {
        DataContext = new MainWindowVM(navigationStore)
    };

    if (startedAutomatically)
    {
        MainWindow.Hide();
        MainWindow.StateChanged += (sender, args) =>
        {
            if (MainWindow.WindowState == WindowState.Normal)
            {
                MainWindow.Show();
            }
        };
    }
    else
    {
        MainWindow.Show();
    }

    AddToStartup();
}

保留原IsStartedAutomatically方法不变。

关键说明

  • 窗口标题替换:FindWindow的第二个参数需要替换为你MainWindow的Title属性值,确保能准确找到后台窗口。
  • API参数说明:ShowWindow的参数9对应SW_RESTORE,用于将最小化或隐藏的窗口恢复到正常状态;SetForegroundWindow负责将窗口带到前台并激活。
  • 代码优化:将MainWindow的实例化逻辑抽离,减少重复代码,提升可维护性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 09:20:39