首页 > 编程语言 >程序的单例模式

程序的单例模式

时间:2022-08-24 12:12:51浏览次数:72  
标签:IntPtr 程序 System 模式 MainWindow 实例 单例 using public

对于一些程序有时需要限制实例个数为单例,如同一时刻,只能有一个实例存在。具体的实现方式主要有互斥锁Mutex和查询进程Process

一、 判断是否已创建对应的实例对象

1)、通过Mutex来判断是否为多实例对象

  1. 首先判断调用的线程是否拥有已初始化的互斥锁,如果true则表示已经存在对应的实例对象了,false表示当前还未创建对应实例对象。
  2. 如果判断true,即已经存在对应的实例对象,则现在正在创建的实例程序将被关闭。
using System.Windows;
using System.Threading;

namespace Simple_Test
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        Mutex myMutex;
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            bool isFirstInstance = false;
            myMutex = new Mutex(true, "MyInstanceApp",out isFirstInstance);
            if(!isFirstInstance)
            {
                // another instance is created,there is already an instance is running

                App.Current.Shutdown();
            }
        }
    }
}

2)、通过检索当前实例的进程数是否大于1来判断

若等于1,则表示只有一个实例对象,若大于1则表示有多个实例对象(包含当前正在创建的这个实例对象)

using System.Windows;
using System.Threading;
using System.Diagnostics;
using System.Linq;

namespace Simple_Test
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            Process process = Process.GetCurrentProcess();
            int count = Process.GetProcesses().Where(p => p.ProcessName == process.ProcessName).Count();
            if (count > 1)
                App.Current.Shutdown();
        }
    }
}

二、 判断是否已存在实例对象,若存在则将该窗口置于顶层

  1. 先判断对应的实例对象是否已经创建。
  2. 若已经创建,则将对应的实例窗口至于显示器顶层,以让User进行操作

1)、Mutex --> PostMessage --> ReceiveMessage

App.xaml.cs 文件中判断对应的实例是否已经存在

using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace SingleInstance
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        Mutex singleMutex;

        private void StartupApp()
        {
            singleMutex = new Mutex(true, "SingleInstance");
            //设置等待时间为0,以防止阻塞
            if (singleMutex.WaitOne(TimeSpan.Zero, true))
            {
                //the first app instance has been created.
                singleMutex.ReleaseMutex();
            }
            else
            {
                // When another app instance is ready to create, posting a message to WndProc.
                WrapWin32Methods.PostMessage((IntPtr)WrapWin32Methods.HWND_BROADCAST,
                    WrapWin32Methods.WM_SHOWME,
                    IntPtr.Zero,
                    IntPtr.Zero);

                // Already application Window will show, and shutdown the current application.
                Application.Current.Shutdown();
            }

        }

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            StartupApp();     
        }

    }
}

WrapWin32Methods.cs 文件中封装一些Win32API

using System;
using System.Threading.Tasks;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media;

//This class just wraps some Win32 methods that we're going to use
namespace SingleInstance
{
    internal class WrapWin32Methods
    {
        public const int HWND_BROADCAST = 0xffff;

        [DllImport("user32")]
        public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
        [DllImport("user32")]
        public static extern int RegisterWindowMessage(string message);

        public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");

        /// <summary>
        /// Method_1: Add an event handler that receives all window messages
        /// </summary>
        /// <param name="wind"></param>
        public void AddHookWndProc(Visual curVisual)
        {
            HwndSource hwndSource= PresentationSource.FromVisual(curVisual) as HwndSource;
            hwndSource.AddHook(WndProc);
        }

        /// <summary>
        /// Method_2: Add an event handler that receives all window messages
        /// </summary>
        /// <param name="curWindow"></param>
        public void AddHookWndProc(Window curWindow)
        {
            if(curWindow!=null)
            {
                IntPtr hwnd = new WindowInteropHelper(curWindow).Handle;
                HwndSource.FromHwnd(hwnd).AddHook(WndProc);
            }
        }


        /// <summary>
        /// Used to handle received windows message.
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="msg"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <param name="handled"></param>
        /// <returns></returns>
        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            // Handle the received message.
            if(msg == WM_SHOWME)
            {
                //MessageBox.Show("Already exist.");
                if (Application.Current.MainWindow.WindowState == WindowState.Minimized)
                {
                    Application.Current.MainWindow.WindowState = WindowState.Normal;
                }
                Application.Current.MainWindow.Topmost = true;

                RestoreState();
            }


            return IntPtr.Zero;
        }


        /// <summary>
        /// Restore the window TopMost to false, otherwise the window will 
        /// will always on the top of other window.
        /// </summary>
        private async void RestoreState()
        {
            await Task.Delay(100);
            Application.Current.MainWindow.Topmost = false;
        }

    }
}


MainWindow.xaml.cs 文件中将封装的Win32 窗口过程函数添加到Hook上,以能抓到对应的消息,即 WM_SHOWME
【注意】AddHook 的调用必须要等到MainWindow完成初始化之后,否则 new WindowInteropHelper(curWindow).Handle 返回值将为0,最终将导致无法AddHook到对应的句柄上。可以将该函数的调用放在程序完全起来之后的某个时机点

using System;
using System.Windows;

namespace SingleInstance
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        
        public MainWindow()
        {
            InitializeComponent();
        }

        protected override void OnContentRendered(EventArgs e)
        {
            base.OnContentRendered(e);

            WrapWin32Methods wrapMethod = new WrapWin32Methods();

            Window curWindow = Application.Current.MainWindow;
            wrapMethod.AddHookWndProc(curWindow);
        }

        // OnSourceInitialized函数中添加 AddHook 也可以
        //protected override void OnSourceInitialized(EventArgs e)
        //{
        //    base.OnSourceInitialized(e);
        //}
    }
}

2)、通过该进程的方式

[DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);


// 首先获取该进程的名称,然后在构造函数或者其它地方调用该函数
private void ShowSpecifiedWindow(string processName)
{
    //将指定的窗口APP放在屏幕最上层
    Process[] getPro = Process.GetProcessesByName(processName);
    if (getPro.Count() > 0)
    {
        if (getPro[0].MainWindowHandle != IntPtr.Zero)
        {
            ShowWindow(getPro[0].MainWindowHandle, SW_SHOWNORMAL);
            SetForegroundWindow(getPro[0].MainWindowHandle);
        }
    }
}


参考资料:

  1. WPF Single Instance Best Practices
  2. What is the correct way to create a single-instance WPF application?

标签:IntPtr,程序,System,模式,MainWindow,实例,单例,using,public
From: https://www.cnblogs.com/Jeffxu/p/16619407.html

相关文章

  • 微信小程序点击结算获取用户信息
    //countPrice代表事件名字countPrice(){//获取用户登录信息用同步的方法获取用户信息userinfo代表键letuserinfo=wx.getStorageSync('......
  • 微信小程序开发工具,更新后报typeof4 is not a function
    升级开发者工具版本后,新版工具将es6转es5和增强编译合并,报以下错误。  解决方法有两种:1.修改一下@babel/runtime/helpers/typeof.js文件,内容修改为代码片段的。......
  • c++单例模式
    单例模式的意图:一个类只能有一个实例。 //非线程安全版本1classSingleton{2private:3Singleton();4Singleton(constSingleton&);5public:......
  • 原生小程序的单向双向绑定--简易双向绑定
    1、在wxml中,普通属性的数据绑定是单向的<inputvalue="{{value}}"/>上面这串代码,可以通过this.setData({value:xxx})去更新值和输入框中显示的值也会更新为xxx,但是如果......
  • linux安装python虚拟环境并启动python程序
    安装python虚拟环境并启动python应用安装虚拟环境#安装virtualenv虚拟环境工具pip37为python命令安装python应用时创建的/usr/bin/pip37pip37installvirtuale......
  • Iterator设计模式
    迭代器设计模式用集合类实现Aggregate接口,并实现其iterator方法,生成特定的迭代器对象,但返回时返回为Iterator类型,以此来抽象编程,实现解耦合的目的。类图:   当使用......
  • 工厂模式-简单工厂
    publicinterfaceCar{Stringexcute();}publicclassBenchiCarimplementsCar{publicStringexcute(){System.out.println("奔驰车生产....");......
  • Netty - NIO 之 Selector模式
    一、总结豁然开朗,之前以为非阻塞的实现是因为selector。现在才知道selector的为了让非阻塞变成更好:无事件时,阻塞有事件时,非阻塞 二、背景知识2.1事件的类型 ......
  • 设计模式10 - 设计模式 - 适配器设计模式(高频-结构型)
    一、定义适配器模式将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能在一起工作的那些类可以一起工作。    适配器模式......
  • Delphi 经典游戏程序设计40例 的学习 例24 xor 的攻击效果
    unitR24;interfaceusesWindows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs,StdCtrls,ExtCtrls;typeTRei24=class......