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

如何修复.NET 8 WPF C#应用单实例?隐藏窗口无法唤起

.NET 8 WPF单实例唤起隐藏窗口问题解决方案

问题概述

为.NET 8 WPF应用实现单实例功能时,窗口处于最小化状态可正常唤起,但处于隐藏状态时无法激活。当前使用Mutex结合Win32 API实现,存在隐藏窗口唤起失效的问题。

现有代码问题分析

  1. 窗口标题依赖:使用FindWindow(null, "أذان ليبيا")通过标题查找窗口,若窗口标题动态变更,会导致找不到句柄。
  2. Win32 API调用逻辑:隐藏窗口的处理分支可能存在执行顺序或参数问题,导致ShowWindow未生效。
  3. 前台窗口权限限制:SetForegroundWindow受系统权限限制,直接调用可能失败。

解决方案一:修复现有Win32 API实现

修改思路

  • 改用进程查找窗口句柄,避免标题依赖
  • 添加权限允许调用,确保SetForegroundWindow生效
  • 调整窗口显示逻辑,优先确保窗口可见再激活

修改后的完整代码

using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;

namespace Adan
{
    public partial class App : System.Windows.Application
    {
        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool IsWindowVisible(IntPtr hWnd);

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("user32.dll", SetLastError = true)]
        static extern bool IsIconic(IntPtr hWnd);

        [DllImport("user32.dll")]
        static extern bool BringWindowToTop(IntPtr hWnd);

        [DllImport("user32.dll", SetLastError = true)]
        static extern bool AllowSetForegroundWindow(uint dwProcessId);

        const int SW_RESTORE = 9;
        const int SW_SHOW = 5;

        private static Mutex mutex;

        protected override void OnStartup(StartupEventArgs e)
        {
            bool createdNew;
            mutex = new Mutex(true, "AdanMutex", out createdNew);

            if (!createdNew)
            {
                var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
                var runningProcess = System.Diagnostics.Process.GetProcessesByName(currentProcess.ProcessName)
                    .FirstOrDefault(p => p.Id != currentProcess.Id);

                if (runningProcess != null && runningProcess.MainWindowHandle != IntPtr.Zero)
                {
                    IntPtr handle = runningProcess.MainWindowHandle;

                    // 允许当前进程设置目标进程的前台窗口
                    AllowSetForegroundWindow((uint)runningProcess.Id);

                    // 先恢复最小化窗口,再显示隐藏窗口
                    if (IsIconic(handle))
                    {
                        ShowWindow(handle, SW_RESTORE);
                    }
                    else if (!IsWindowVisible(handle))
                    {
                        ShowWindow(handle, SW_SHOW);
                    }

                    // 确保窗口置顶并激活
                    BringWindowToTop(handle);
                    SetForegroundWindow(handle);
                }
                else
                {
                    ShowAlreadyRunningMessage();
                }

                Shutdown();
                return;
            }

            base.OnStartup(e);
        }

        private void ShowAlreadyRunningMessage()
        {
            Border border = new Border
            {
                Width = 300,
                Height = 100,
                CornerRadius = new CornerRadius(20),
                Background = System.Windows.Media.Brushes.White,
                Child = new TextBlock
                {
                    Text = "البرنامج يعمل بالفعل",
                    FontSize = 16,
                    HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
                    VerticalAlignment = System.Windows.VerticalAlignment.Center
                }
            };

            Window messageWindow = new Window
            {
                Width = 300,
                Height = 100,
                WindowStartupLocation = WindowStartupLocation.CenterScreen,
                WindowStyle = WindowStyle.None,
                AllowsTransparency = true,
                Background = System.Windows.Media.Brushes.Transparent,
                Content = border
            };
            messageWindow.Show();

            DispatcherTimer timer = new DispatcherTimer
            {
                Interval = TimeSpan.FromSeconds(20)
            };
            timer.Tick += (sender, args) =>
            {
                timer.Stop();
                messageWindow.Close();
            };
            timer.Start();
        }

        protected override void OnExit(ExitEventArgs e)
        {
            if (mutex != null)
            {
                if (mutex.WaitOne(0))
                {
                    mutex.ReleaseMutex();
                }
                mutex.Dispose();
            }

            base.OnExit(e);
        }
    }
}

解决方案二:使用.NET自带的单实例组件

实现步骤

  1. 右键项目 -> 添加 -> 引用 -> 勾选Microsoft.VisualBasic
  2. 替换App类实现,利用WindowsFormsApplicationBase自带的单实例管理

完整代码

using System;
using System.Windows;
using Microsoft.VisualBasic.ApplicationServices;

namespace Adan
{
    public partial class App : Application
    {
        private SingleInstanceManager _instanceManager;

        protected override void OnStartup(StartupEventArgs e)
        {
            _instanceManager = new SingleInstanceManager();
            _instanceManager.Run(e.Args);
        }
    }

    public class SingleInstanceManager : WindowsFormsApplicationBase
    {
        public SingleInstanceManager()
        {
            IsSingleInstance = true;
        }

        // 当新实例启动时触发,处理窗口唤起逻辑
        protected override bool OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
        {
            if (App.Current.MainWindow != null)
            {
                var mainWindow = App.Current.MainWindow;
                
                // 恢复最小化窗口
                if (mainWindow.WindowState == WindowState.Minimized)
                {
                    mainWindow.WindowState = WindowState.Normal;
                }
                
                // 显示隐藏窗口并激活
                mainWindow.Show();
                mainWindow.Activate();
            }
            return true;
        }

        protected override void OnCreateMainForm()
        {
            // 占位Form,WPF主窗口由Application自动管理
            MainForm = new System.Windows.Forms.Form();
            App.Current.Run();
        }
    }
}

这种方式无需手动处理Mutex和Win32 API,依赖.NET自带组件,稳定性更高。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 13:32:35