在.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;
    }
}

.net相关问答推荐

将Visual Studio更新到v17.9.3后,IDE关闭,dotnet.exe命令报告致命错误.内部CLR错误.(0x80131506)

如何按需计算一个值并将该值缓存在FP/F#中?

NETSDK1083:无法识别指定的 RuntimeIdentifierwin10-x64

"投掷;" 是什么意思?靠自己做什么?

Silverlight 与 Flex

为什么需要 XmlNamespaceManager?

如何判断一个类型是否是简单类型?即持有一个单一的价值

C# 编译为 32/64 位,或任何 cpu?

关闭 Visual Studio 中所有选项卡但当前选项卡的键盘快捷键?

ASP.NET Core (.NET Core) 和 ASP.NET Core (.NET Framework) 的区别

Iif 在 C# 中等效

如何找到二维数组的大小?

从流中获取 TextReader?

如何使用 EPPlus 设置 XLSX 单元格宽度?

如何在安装后立即启动 .NET Windows 服务?

对构造函数进行单元测试重要吗?

在不使用while循环的情况下找到最里面的异常?

ADO.NET Entity Framework:更新向导不会添加表

如何重新启动我的 C# WinForm 应用程序?

模拟和单元测试需要时如何抛出 SqlException?