Tom*_*zzo 36 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
}
}
Run Code Online (Sandbox Code Playgroud)
一切都很完美......但我对它有一些疑问,我想收到你关于如何改进我的方法的建议.
1)Code Analysis要求我实现IDisposable接口,因为我使用的是IDisposable成员(the Mutex).我的Dispose()实施是否足够好?我应该避免它,因为它永远不会被调用吗?
2)最好使用m_Mutex = new Mutex(true, mutexName, out mutexCreated);并检查结果或使用m_Mutex = new Mutex(false, mutexName);然后检查m_Mutex.WaitOne(TimeSpan.Zero, false);?在多线程的情况下,我的意思是......
3)RegisterWindowMessageAPI调用应该返回UInt32...但是HwndSourceHook只接受Int32消息值...我应该担心意外行为(比如结果大于Int32.MaxValue)?
4)在OnStartup覆盖... base.OnStartup(e);即使另一个实例已在运行我应该执行并且我要关闭应用程序吗?
5)有没有更好的方法将现有实例置于不需要设置Topmost值的顶部?也许Activate()?
6)你能看到我的方法有什么缺陷吗?关于多线程,坏的异常处理和类似的东西?例如......如果我的应用程序在OnStartup和之间崩溃会发生什么OnExit?
C-v*_*-va 44
有几种选择,
使用侦听器套接字
互斥
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();
}
}
Run Code Online (Sandbox Code Playgroud)
流程经理
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();
}
}
Run Code Online (Sandbox Code Playgroud)使用侦听器套接字
向另一个应用程序发出信号的一种方法是打开一个Tcp连接.创建套接字,绑定到端口,然后在后台线程上侦听连接.如果成功,请正常运行.如果没有,则建立与该端口的连接,该端口向另一个实例发出第二次应用程序启动尝试的信号.如果合适,原始实例可以将其主窗口置于前面.
"安全"软件/防火墙可能是一个问题.
Zak*_*iMa 35
我希望有一个更好的用户体验 - 如果另一个实例已经在运行,让我们激活它,而不是显示有关第二个实例的错误.这是我的实施.
我使用命名的Mutex来确保只有一个实例正在运行并命名为EventWaitHandle以将通知从一个实例传递到另一个实例.
App.xaml.cs:
/// <summary>Interaction logic for App.xaml</summary>
public partial class App
{
#region Constants and Fields
/// <summary>The event mutex name.</summary>
private const string UniqueEventName = "{GUID}";
/// <summary>The unique mutex name.</summary>
private const string UniqueMutexName = "{GUID}";
/// <summary>The event wait handle.</summary>
private EventWaitHandle eventWaitHandle;
/// <summary>The mutex.</summary>
private Mutex mutex;
#endregion
#region Methods
/// <summary>The app on startup.</summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The e.</param>
private void AppOnStartup(object sender, StartupEventArgs e)
{
bool isOwned;
this.mutex = new Mutex(true, UniqueMutexName, out isOwned);
this.eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, UniqueEventName);
// So, R# would not give a warning that this variable is not used.
GC.KeepAlive(this.mutex);
if (isOwned)
{
// Spawn a thread which will be waiting for our event
var thread = new Thread(
() =>
{
while (this.eventWaitHandle.WaitOne())
{
Current.Dispatcher.BeginInvoke(
(Action)(() => ((MainWindow)Current.MainWindow).BringToForeground()));
}
});
// It is important mark it as background otherwise it will prevent app from exiting.
thread.IsBackground = true;
thread.Start();
return;
}
// Notify other instance so it could bring itself to foreground.
this.eventWaitHandle.Set();
// Terminate this instance.
this.Shutdown();
}
#endregion
}
Run Code Online (Sandbox Code Playgroud)
和MainWindow.cs中的BringToForeground:
/// <summary>Brings main window to foreground.</summary>
public void BringToForeground()
{
if (this.WindowState == WindowState.Minimized || this.Visibility == Visibility.Hidden)
{
this.Show();
this.WindowState = WindowState.Normal;
}
// According to some sources these steps gurantee that an app will be brought to foreground.
this.Activate();
this.Topmost = true;
this.Topmost = false;
this.Focus();
}
Run Code Online (Sandbox Code Playgroud)
并添加Startup ="AppOnStartup"(谢谢vhanla!):
<Application x:Class="MyClass.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="AppOnStartup">
<Application.Resources>
</Application.Resources>
</Application>
Run Code Online (Sandbox Code Playgroud)
适合我:)
sme*_*asn 30
对于WPF,只需使用:
public partial class App : Application
{
private static Mutex _mutex = null;
protected override void OnStartup(StartupEventArgs e)
{
const string appName = "MyAppName";
bool createdNew;
_mutex = new Mutex(true, appName, out createdNew);
if (!createdNew)
{
//app is already running! Exiting the application
Application.Current.Shutdown();
}
base.OnStartup(e);
}
}
Run Code Online (Sandbox Code Playgroud)
1)它看起来像我的标准Dispose实现.这不是必要的(见第6点),但它没有任何伤害.(清理关闭它有点像清理房子,然后烧掉它,恕我直言,但对此事的意见不同......)
无论如何,为什么不使用"Dispose"作为清理方法的名称,即使它没有被直接调用?您可以将其称为"清理",但请记住您也为人类编写代码,Dispose看起来很熟悉,.NET上的任何人都了解它的用途.所以,去"Dispose".
2)我一直认为,m_Mutex = new Mutex(false, mutexName);我认为这更像是一项技术优势的惯例.
3)来自MSDN:
如果消息成功注册,则返回值是0xC000到0xFFFF范围内的消息标识符.
所以我不担心.通常,对于这类函数,UInt不用于"它不适合Int,让我们使用UInt所以我们有更多东西"但澄清合同"函数永远不会返回负值".
4)如果你关机,我会避免打电话,原因与#1相同
5)有几种方法可以做到这一点.Win32中最简单的方法就是让第二个实例调用SetForegroundWindow(在这里查看:http://blogs.msdn.com/b/oldnewthing/archive/2009/02/20/9435239.aspx); 但是,我不知道是否有等效的WPF功能,或者你是否需要PInvoke它.
6)
例如......如果我的应用程序在OnStartup和OnExit之间崩溃会发生什么?
没关系:当进程终止时,进程拥有的所有句柄都被释放; 互斥体也被释放了.
总之,我的建议:
例如,您可以使用您的技术(尝试向窗口发送/发送消息 - 如果没有回复它被卡住),加上MSK技术,以查找和终止旧进程.然后正常开始.
防止第二个实例(并发出现有信号),
可以这样做(这适用于 WPF 应用程序(参见 App() 的参考),但也适用于 WinForms):
public partial class App : Application
{
public App()
{
// initiate it. Call it first.
preventSecond();
}
private const string UniqueEventName = "{GENERATE-YOUR-OWN-GUID}";
private void preventSecond()
{
try
{
EventWaitHandle.OpenExisting(UniqueEventName); // check if it exists
this.Shutdown();
}
catch (WaitHandleCannotBeOpenedException)
{
new EventWaitHandle(false, EventResetMode.AutoReset, UniqueEventName); // register
}
}
}
Run Code Online (Sandbox Code Playgroud)
第二个版本:上面加上信号另一个实例来显示窗口(更改 WinForms 的 MainWindow 部分):
public partial class App : Application
{
public App()
{
// initiate it. Call it first.
//preventSecond();
SingleInstanceWatcher();
}
private const string UniqueEventName = "{GENERATE-YOUR-OWN-GUID}";
private EventWaitHandle eventWaitHandle;
/// <summary>prevent a second instance and signal it to bring its mainwindow to foreground</summary>
/// <seealso cref="/sf/answers/1661110251/"/>
private void SingleInstanceWatcher()
{
// check if it is already open.
try
{
// try to open it - if another instance is running, it will exist , if not it will throw
this.eventWaitHandle = EventWaitHandle.OpenExisting(UniqueEventName);
// Notify other instance so it could bring itself to foreground.
this.eventWaitHandle.Set();
// Terminate this instance.
this.Shutdown();
}
catch (WaitHandleCannotBeOpenedException)
{
// listen to a new event (this app instance will be the new "master")
this.eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, UniqueEventName);
}
// if this instance gets the signal to show the main window
new Task(() =>
{
while (this.eventWaitHandle.WaitOne())
{
Current.Dispatcher.BeginInvoke((Action)(() =>
{
// could be set or removed anytime
if (!Current.MainWindow.Equals(null))
{
var mw = Current.MainWindow;
if (mw.WindowState == WindowState.Minimized || mw.Visibility != Visibility.Visible)
{
mw.Show();
mw.WindowState = WindowState.Normal;
}
// According to some sources these steps are required to be sure it went to foreground.
mw.Activate();
mw.Topmost = true;
mw.Topmost = false;
mw.Focus();
}
}));
}
})
.Start();
}
}
Run Code Online (Sandbox Code Playgroud)
此代码作为类中的下降,将是@ Selfcontained-C-Sharp-WPF-compatible-utility-classes / Utils.SingleInstance.cs
小智 5
处理它的最直接的方法是使用命名信号量.尝试这样的事......
public partial class App : Application
{
Semaphore sema;
bool shouldRelease = false;
protected override void OnStartup(StartupEventArgs e)
{
bool result = Semaphore.TryOpenExisting("SingleInstanceWPFApp", out sema);
if (result) // we have another instance running
{
App.Current.Shutdown();
}
else
{
try
{
sema = new Semaphore(1, 1, "SingleInstanceWPFApp");
}
catch
{
App.Current.Shutdown(); //
}
}
if (!sema.WaitOne(0))
{
App.Current.Shutdown();
}
else
{
shouldRelease = true;
}
base.OnStartup(e);
}
protected override void OnExit(ExitEventArgs e)
{
if (sema != null && shouldRelease)
{
sema.Release();
}
}
}
Run Code Online (Sandbox Code Playgroud)
我的 .Net Core 3 Wpf 单实例应用程序解决方案:
[STAThread]
public static void Main()
{
StartSingleInstanceApplication<CntApplication>();
}
public static void StartSingleInstanceApplication<T>()
where T : RichApplication
{
DebuggerOutput.GetInstance();
Assembly assembly = typeof(T).Assembly;
string mutexName = $"SingleInstanceApplication/{assembly.GetName().Name}/{assembly.GetType().GUID}";
Mutex mutex = new Mutex(true, mutexName, out bool mutexCreated);
if (!mutexCreated)
{
mutex = null;
var client = new NamedPipeClientStream(mutexName);
client.Connect();
using (StreamWriter writer = new StreamWriter(client))
writer.Write(string.Join("\t", Environment.GetCommandLineArgs()));
return;
}
else
{
T application = Activator.CreateInstance<T>();
application.Exit += (object sender, ExitEventArgs e) =>
{
mutex.ReleaseMutex();
mutex.Close();
mutex = null;
};
Task.Factory.StartNew(() =>
{
while (mutex != null)
{
using (var server = new NamedPipeServerStream(mutexName))
{
server.WaitForConnection();
using (StreamReader reader = new StreamReader(server))
{
string[] args = reader.ReadToEnd().Split("\t", StringSplitOptions.RemoveEmptyEntries).ToArray();
UIDispatcher.GetInstance().Invoke(() => application.ExecuteCommandLineArgs(args));
}
}
}
}, TaskCreationOptions.LongRunning);
typeof(T).GetMethod("InitializeComponent").Invoke(application, new object[] { });
application.Run();
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
42734 次 |
| 最近记录: |