• 让 WPF 应用程序单例化


    在 WPF 程序的开发中,经常需要防止应用程序重复运行,但是 WPF 没有提供自带的解决方案,那么如何实现应用程序的单例化呢?

    网上流行一种简单粗暴的方法,检测进程名,如果有同名的进程,就 Shutdown:

    Process process = Process.GetCurrentProcess();
    foreach (Process p in Process.GetProcessesByName(process.ProcessName))
    {
        if (p.Id != process.Id)
        {
            Shutdown();
            return;
        }
    }
    View Code

    这种方法非常不安全,但在一般使用环境中,我们自己的程序还是比较难与其他进程重名的,因此这种方法还是有一定的实用性。

    更保险的方法是使用 Mutex:

    Mutex mutex = new Mutex(true, Assembly.GetExecutingAssembly().GetName().Name, out bool createdNew);
    if (createdNew)
    {
         base.OnStartup(e);
    }
    else
    {
         Shutdown();
    }

    System.Threading.Mutex 专门用于进程同步,在这三个参数中,第一个表示获得 Mutex 的所有权;第二个是 Mutex 的名字,第三个表示是否新创建了 Mutex。由于名字可以是任意字符串,因此大大提高了程序的兼容性。

    对于绝大多数单实例应用程序,上述两种方法都够用了,但如果想要构建类似于 Office、VS 等多窗口程序就不行了。如何降低应用程序开销、集中某些特性(例如创建单独的打印队列管理器)或集成不同窗口(例如平铺当前打开的文档窗口)?构建一个真正的单实例应用程序是最佳选择。

    但最简单同时也是 WPF 团队推荐的方法是:使用 Windows Form 提供的内置支持,这一内置支持最初是用于 Visual Basic 应用程序的。这种方法在后台处理杂乱的问题。

    1. 创建单实例应用程序封装器

    首先添加 Microsoft.VisualBasic.dll 的引用,并从 Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase 类继承自定义类。WindowsFormsApplicationBase 类提供了三个用于管理实例的重要成员:

    • IsSingleInstance 属性启用单实例应用程序。在构造函数中将该属性设置为 true。
    • 当应用程序启动时触发的 OnStartup() 方法。此时重写该方法并创建 WPF 应用程序对象。
    • 当另一个应用程序实例启动时触发的 OnStartupNextInstance() 方法。该方法提供了访问命令行参数的功能。此时,可调用 WPF 应用程序类中的方法来显示新的窗口,但不创建另一个应用程序对象。

    下面是派生类的代码:

    class SingleInstanceAppWrapper : WindowsFormsApplicationBase
    {
        public SingleInstanceAppWrapper()
        {
            // Enable single-instance mode.
            this.IsSingleInstance = true;
        }
    
        // Create the Wpf application class.
        private App app;
        protected override bool OnStartup(StartupEventArgs eventArgs)
        {
            app = new App();
            app.Run();
            return false;
        }
    
        // Direct multiple instances.
        protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
        {
            if (eventArgs.CommandLine.Count > 0)
            {
                app.Process(eventArgs.CommandLine[0]);
            }
        }
    }
    View Code

    当应用程序启动时,该类创建我们的平时使用的 App 类,该类可以添加一些方法,接收后续实例的启动参数,并显示新的窗口等等。

    我们还需要切换应用程序的入口,由于需要在 App 类之前创建 SingleInstanceAppWrapper 类,所有应用程序必须使用传统的 Main() 方法来启动,而不能使用 App.xaml 文件:

    public class Startup
    {
        [STAThread]
        public static void Main(string[] args)
        {
            SingleInstanceAppWrapper wrapper = new SingleInstanceAppWrapper();
            wrapper.Run(args);
        }
    }
    View Code

    然后修改项目属性的“启动对象”为 Startup 类:

  • 相关阅读:
    NOIP2018游记
    NOIP2018T1DAY1——Road(并查集做法??)
    UVA11021 Tribles——概率dp
    捡石头——(期望递推)
    USACO2008mar-gold牛跑步(第k短路:A-star)
    Java中的异常处理
    Java学习手册
    各种应用层注入手段整理 【转载】
    正则表达式学习
    Run-Time Check Failure #0,The value of ESP was not properly saved 错误解决
  • 原文地址:https://www.cnblogs.com/heimao233/p/10414516.html
Copyright © 2020-2023  润新知