c# - WPF 单实例最佳实践

标签 c# wpf singleton mutex

这是我迄今为止为创建单个实例 WPF 应用程序而实现的代码:

#region Using Directives
using System;
using System.Globalization;
using System.Reflection;
using System.Threading;
using System.Windows;
using System.Windows.Interop;
#endregion

namespace MyWPF
{
    public partial class MainApplication : Application, IDisposable
    {
        #region Members
        private Int32 m_Message;
        private Mutex m_Mutex;
        #endregion

        #region Methods: Functions
        private IntPtr HandleMessages(IntPtr handle, Int32 message, IntPtr wParameter, IntPtr lParameter, ref Boolean handled)
        {
            if (message == m_Message)
            {
                if (MainWindow.WindowState == WindowState.Minimized)
                    MainWindow.WindowState = WindowState.Normal;

                Boolean topmost = MainWindow.Topmost;

                MainWindow.Topmost = true;
                MainWindow.Topmost = topmost;
            }

            return IntPtr.Zero;
        }

        private void Dispose(Boolean disposing)
        {
            if (disposing && (m_Mutex != null))
            {
                m_Mutex.ReleaseMutex();
                m_Mutex.Close();
                m_Mutex = null;
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion

        #region Methods: Overrides
        protected override void OnStartup(StartupEventArgs e)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            Boolean mutexCreated;
            String mutexName = String.Format(CultureInfo.InvariantCulture, "Local\\{{{0}}}{{{1}}}", assembly.GetType().GUID, assembly.GetName().Name);

            m_Mutex = new Mutex(true, mutexName, out mutexCreated);
            m_Message = NativeMethods.RegisterWindowMessage(mutexName);

            if (!mutexCreated)
            {
                m_Mutex = null;

                NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, m_Message, IntPtr.Zero, IntPtr.Zero);

                Current.Shutdown();

                return;
            }

            base.OnStartup(e);

            MainWindow window = new MainWindow();
            MainWindow = window;
            window.Show(); 

            HwndSource.FromHwnd((new WindowInteropHelper(window)).Handle).AddHook(new HwndSourceHook(HandleMessages));
        }

        protected override void OnExit(ExitEventArgs e)
        {
            Dispose();
            base.OnExit(e);
        }
        #endregion
    }
}

一切都很完美......但我对此有一些疑问,我想收到您关于如何改进我的方法的建议。

1) 代码分析要求我实现 IDisposable 接口(interface),因为我使用的是 IDisposable 成员(Mutex)。我的 Dispose() 实现是否足够好?我应该避免它,因为它永远不会被调用吗?

2) 最好使用 m_Mutex = new Mutex(true, mutexName, out mutexCreated); 并检查结果或使用 m_Mutex = new Mutex(false, mutexName); 然后检查 m_Mutex.WaitOne(TimeSpan.Zero, false); ?在多线程的情况下,我的意思是......

3) RegisterWindowMessage API 调用应该返回 UInt32...但是 HwndSourceHook 只接受 Int32 作为消息value... 我应该担心意外行为(比如结果大于 Int32.MaxValue)吗?

4) 在 OnStartup 覆盖...我应该执行 base.OnStartup(e); 即使另一个实例已经在运行并且我要关闭申请?

5) 是否有更好的方法将现有实例带到顶部而不需要设置 Topmost 值?也许 Activate()

6) 你能看出我的方法有什么缺陷吗?关于多线程、不良异常处理之类的事情?例如...如果我的应用程序在 OnStartupOnExit 之间崩溃会怎样?

最佳答案

有多种选择,

  • 互斥体
  • 流程经理
  • 命名信号量
  • 使用监听套接字

互斥锁

Mutex myMutex ;

private void Application_Startup(object sender, StartupEventArgs e)
{
    bool aIsNewInstance = false;
    myMutex = new Mutex(true, "MyWPFApplication", out aIsNewInstance);  
    if (!aIsNewInstance)
    {
        MessageBox.Show("Already an instance is running...");
        App.Current.Shutdown();  
    }
}

流程经理

private void Application_Startup(object sender, StartupEventArgs e)
{
    Process proc = Process.GetCurrentProcess();
    int count = Process.GetProcesses().Where(p=> 
        p.ProcessName == proc.ProcessName).Count();

    if (count > 1)
    {
        MessageBox.Show("Already an instance is running...");
        App.Current.Shutdown(); 
    }
}

使用监听器套接字

向另一个应用程序发送信号的一种方法是打开一个到它的 Tcp 连接。创建一个套接字,绑定(bind)到一个端口,并在后台线程上监听连接。如果成功,则正常运行。如果没有,则连接到该端口,这会向另一个实例发出信号表明已进行第二次应用程序启动尝试。然后,如果合适,原始实例可以将其主窗口置于最前面。

“安全”软件/防火墙可能是个问题。

Single Instance Application C#.Net along with Win32

关于c# - WPF 单实例最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14506406/

相关文章:

c# - WPF 数据绑定(bind) TabItem header

objective-c - iOS 5 中的单例?

java - 在 Java 接口(interface)中使用单例

c# - '<>c.<>9' 是什么意思?我找不到它们的任何声明,它们来自哪里?

c# - 具有 O(n) 迭代,O(1) 包含和 O(1) 通过键获取的数据结构

wpf - 编辑标准 WPF Aero 主题

wpf - 将 VS2010 项目加载到 Expression Blend 时出现属性无法识别或无法访问错误

python 单例与类方法

c# - MVC3 中更新局部 View 的 SetTimeout 随机执行

c# - 在 Mono for Windows 中编译的程序可以在 Mono for Linux 中开箱即用吗?