这是我迄今为止为创建单个实例 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) 你能看出我的方法有什么缺陷吗?关于多线程、不良异常处理之类的事情?例如...如果我的应用程序在 OnStartup
和 OnExit
之间崩溃会怎样?
最佳答案
有多种选择,
- 互斥体
- 流程经理
- 命名信号量
- 使用监听套接字
互斥锁
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)到一个端口,并在后台线程上监听连接。如果成功,则正常运行。如果没有,则连接到该端口,这会向另一个实例发出信号表明已进行第二次应用程序启动尝试。然后,如果合适,原始实例可以将其主窗口置于最前面。
“安全”软件/防火墙可能是个问题。
关于c# - WPF 单实例最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14506406/