在.NET(而不是Windows Forms或控制台)下使用C#和WPF,创建只能作为单个实例运行的应用程序的正确方式是什么?
我知道这和某种神秘的叫做互斥体的东西有关,很少有人会费心停下来解释其中一个是什么.
代码还需要通知已经运行的实例,用户试图启动第二个实例,并且可能还需要传递任何命令行参数(如果存在).
在.NET(而不是Windows Forms或控制台)下使用C#和WPF,创建只能作为单个实例运行的应用程序的正确方式是什么?
我知道这和某种神秘的叫做互斥体的东西有关,很少有人会费心停下来解释其中一个是什么.
代码还需要通知已经运行的实例,用户试图启动第二个实例,并且可能还需要传递任何命令行参数(如果存在).
这里是一个非常好的关于互斥解决方案的article.本文描述的方法有两个优点.
首先,它不需要依赖于Microsoft.VisualBasic程序集.如果我的项目已经依赖于该程序集,我可能会提倡使用方法shown in another answer.但实际上,我不使用Microsoft.VisualBasic程序集,并且我不想向我的项目添加不必要的依赖项.
其次,本文展示了当用户try 启动另一个实例时,如何将应用程序的现有实例带到前台.这是这里描述的其他Mutex解决方案没有解决的一个非常好的做法.
截至2014年8月1日,我在上面链接的文章仍然活跃,但博客已经有一段时间没有更新了.这让我担心,它最终可能会消失,随之而来的是所倡导的解决方案.我在这里为后代复制这篇文章的内容.这些文字只属于Sanity Free Coding岁的博客所有者.
今天我想重构一些禁止我的应用程序的代码
以前我用System.Diagnostics.Process来搜索
我知道我可以使用互斥体来做这件事(但是我从来没有这样做过 之前)我开始减少我的代码,简化我的生活.
在应用程序main的类中,我创建了一个名为Mutex的静态:
static class Program
{
static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
[STAThread]
...
}
有了一个命名的互斥体,我们就可以跨
Mutex.WaitOne具有为我们指定时间量的重载 等待.因为我们实际上并不想同步我们的代码 (更多只需判断它当前是否正在使用)我们将重载与 两个参数:Mutex.WaitOne(Timespan timeout, bool exitContext). 如果能够进入,则等待1返回TRUE,如果不能进入,则返回FALSE. 在这种情况下,我们根本不想等待;如果我们的互斥体 已使用,跳过它,然后继续前进,因此我们传入Timespan.Zero(等待0 毫秒),并将exitContext设置为true,这样我们就可以退出 同步上下文,然后再try 获取它的锁.使用 在这里,我们包装我们的应用程序.在如下代码中运行代码:
static class Program
{
static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
[STAThread]
static void Main() {
if(mutex.WaitOne(TimeSpan.Zero, true)) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
mutex.ReleaseMutex();
} else {
MessageBox.Show("only one instance at a time");
}
}
}
因此,如果我们的应用程序正在运行,WaitOne将返回False,我们将获得 消息框.
我没有显示消息框,而是 Select 使用一个小Win32来 通知我的运行实例有人忘记它已经 运行(通过将其自身置于所有其他窗口的顶部).至 要做到这一点,我用PostMessage向每个人广播了一条自定义消息 窗口(自定义消息已注册到RegisterWindowMessage 通过我正在运行的应用程序,这意味着只有我的应用程序知道 它是),然后我的第二个实例退出.正在运行的应用程序实例 会收到通知并处理它.为了做到这一点,我 在我的主窗体中重写WndProc,并侦听我的自定义 通知.当我收到通知时,我设置了表单的 将TOPTOST属性设置为TRUE可将其置于顶部.
以下是我最终得到的结论:
- 程序反恐精英
static class Program
{
static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
[STAThread]
static void Main() {
if(mutex.WaitOne(TimeSpan.Zero, true)) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
mutex.ReleaseMutex();
} else {
// send our Win32 message to make the currently running instance
// jump on top of all the other windows
NativeMethods.PostMessage(
(IntPtr)NativeMethods.HWND_BROADCAST,
NativeMethods.WM_SHOWME,
IntPtr.Zero,
IntPtr.Zero);
}
}
}
- 本土方法.反恐精英
// this class just wraps some Win32 stuff that we're going to use
internal class NativeMethods
{
public const int HWND_BROADCAST = 0xffff;
public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
}
- Form1.cs(正面部分)
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
if(m.Msg == NativeMethods.WM_SHOWME) {
ShowMe();
}
base.WndProc(ref m);
}
private void ShowMe()
{
if(WindowState == FormWindowState.Minimized) {
WindowState = FormWindowState.Normal;
}
// get our current "TopMost" value (ours will always be false though)
bool top = TopMost;
// make our form jump to the top of everything
TopMost = true;
// set it back to whatever it was
TopMost = top;
}
}