阻止.NET中给定应用程序的多个实例?


123

在.NET中,阻止一个应用程序的多个实例同时运行的最佳方法是什么?而且,如果没有“最佳”技术,则每种解决方案要考虑哪些注意事项?

Answers:


151

使用Mutex。上面使用GetProcessByName的示例之一有很多警告。这是一篇关于该主题的好文章:

http://odetocode.com/Blogs/scott/archive/2004/08/20/401.aspx

[STAThread]
static void Main() 
{
   using(Mutex mutex = new Mutex(false, "Global\\" + appGuid))
   {
      if(!mutex.WaitOne(0, false))
      {
         MessageBox.Show("Instance already running");
         return;
      }

      Application.Run(new Form1());
   }
}

private static string appGuid = "c0a76b5a-12ab-45c5-b9d9-d693faa6e7b9";

1
使用互斥锁也适用于非.net代码(尽管语法会有所不同)
crashmstr

8
这是一个稍微填满的版本,有一些不错的评论:stackoverflow.com/questions/229565/…–
理查德·沃森

2
@ClarkKent:只是一个随机字符串,这样互斥对象的名称就不会与另一个应用程序的名称冲突。
jgauffin

这也可以限制为一定数量的实例吗?
Alejandro delRío

3
为了更好地控制版本或更改应用程序guid的使用,可以使用:string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), true)[0]).Value;这将获取执行程序集的guid
ciosoriog

22
if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1)
{
  AppLog.Write("Application XXXX already running. Only one instance of this application is allowed", AppLog.LogMessageType.Warn);
  return;
}


@SubmarineX您如何确定本文正确?在互斥体和codeproject.com/Articles/4975/…
John Nguyen

十分感谢您的时间和精力
艾哈迈德·马哈茂德

2
-1虽然这是快速的解决方案,但是规避a.exe可以重命名为b.exe并同时运行它们也非常容易。+其他情况下,这根本无法按预期工作。请谨慎使用!
拉尔斯·尼尔森

这不适用于mono-develop。在Windows环境下,效果很好。
Tono Nam

20

这是您需要确保仅运行一个实例的代码。这是使用命名互斥锁的方法。

public class Program
{
    static System.Threading.Mutex singleton = new Mutex(true, "My App Name");

    static void Main(string[] args)
    {
        if (!singleton.WaitOne(TimeSpan.Zero, true))
        {
            //there is already another instance running!
            Application.Exit();
        }
    }
}

2
对于WPF应用程序,请使用Application.Current.Shutdown();。这种方法就像一个魅力。感谢Terrapin。
杰夫(Jeff)

这里最主要的是使互斥体成为静态。在其他情况下,GC也会收集它。
Oleksii 2015年

我喜欢名为Mutex的简单明了。该代码简洁有效。
乍得

7

Hanselman 发表了有关使用Microsoft.VisualBasic程序集的WinFormsApplicationBase类进行此操作的文章。


我已经使用了几年,但现在正在寻求更改为基于Mutex的解决方案。我有客户报告此问题,并且我怀疑它正在使用Remoting来做到这一点。
理查德·沃森

5

到目前为止,似乎已经提出了3种基本技术。

  1. 从Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase类派生并将IsSingleInstance属性设置为true。(我认为这里需要说明的是,这不适用于WPF应用程序,对吗?)
  2. 使用命名的互斥锁并检查它是否已经创建。
  3. 获取正在运行的进程的列表,并比较这些进程的名称。(这有一个警告,要求您的进程名称相对于在给定用户计算机上运行的任何其他进程是唯一的。)

有什么需要注意的事项吗?


3
我认为3并不是非常有效。我会为Mutex投票,多次使用它都没有问题。我从未使用过项1,不确定在使用C#时的情况。
typemismatch

2
选项1仍可与WPF一起使用,只是涉及更多。msdn.microsoft.com/zh-CN/library/ms771662.aspx
Graeme Bradbury,

5

1-在program.cs中创建引用->

using System.Diagnostics;

2- void Main()输入第一行代码->

 if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length >1)
                return;

而已。


这和有什么区别Mutex?有收获吗?
Harambe攻击直升机

它使用进程的名称。如果名称重复,则会生成一个错误标志。在任何其他情况下,它都比互斥体更干净
magallanes

4

在为可执行文件创建项目时使用Visual Studio 2005或2008,在“应用程序”面板内的属性窗口上,有一个名为“制作单实例应用程序”的复选框,您可以激活该复选框以在单实例应用程序上转换应用程序。

这是我正在谈论的窗口的捕获: 在此处输入图片说明 这是一个Visual Studio 2008 Windows应用程序项目。


3
我为您的C#/ WPF应用程序寻找了您提到的此复选框,但没有任何复选框。
HappyNomad 2010年

3
我在VS 2008 C#/ WinForms应用程序的属性中也看不到它。
杰西·麦格鲁

VS2005中也不是。他一定是在提到老VB工作室。
nawfal

是的,该选项存在,我修改了帖子以添加一个窗口捕获,您可以在其中找到此选项。
2011年

6
该选项仅适用于VB.NET应用程序,不适用于C#。显然,该选项本身使用Microsoft.VisualBasic程序集中的WinFormsApplicationBase类。
2011年

4

我在这里尝试了所有解决方案,但在我的C#.net 4.0项目中没有任何效果。希望在这里为某人提供对我有用的解决方案:

作为主类变量:

private static string appGuid = "WRITE AN UNIQUE GUID HERE";
private static Mutex mutex;

当您需要检查应用程序是否已在运行时:

bool mutexCreated;
mutex = new Mutex(true, "Global\\" + appGuid, out mutexCreated);
if (mutexCreated)
    mutex.ReleaseMutex();

if (!mutexCreated)
{
    //App is already running, close this!
    Environment.Exit(0); //i used this because its a console app
}

我只需要在某些条件下关闭其他位置,这对于我的目的来说效果很好


您不需要实际获取互斥锁并将其释放。您只需要知道另一个应用程序是否已经创建了对象(即,其内核引用计数> = 1)。
Michael Goldshteyn 2014年


3

尝试多种解决方案后,我的问题。我最终在这里使用了WPF的示例:http : //www.c-sharpcorner.com/UploadFile/f9f215/how-to-restrict-the-application-to-just-one-instance/

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();  
        }  

    }          
}  

在App.xaml中:

x:Class="*YourNameSpace*.App"
StartupUri="MainWindow.xaml"
Startup="App_Startup"


2

使用VB.NET!不完全是 ;)

使用Microsoft.VisualBasic.ApplicationServices;

VB.Net的WindowsFormsApplicationBase为您提供了“ SingleInstace”属性,该属性确定其他实例,并且仅运行一个实例。


2

这是VB.Net的代码

Private Shared Sub Main()
    Using mutex As New Mutex(False, appGuid)
        If Not mutex.WaitOne(0, False) Then
              MessageBox.Show("Instance already running", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return
        End If

        Application.Run(New Form1())
    End Using
End Sub

这是C#的代码

private static void Main()
{
    using (Mutex mutex = new Mutex(false, appGuid)) {
        if (!mutex.WaitOne(0, false)) {
            MessageBox.Show("Instance already running", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return;
        }

        Application.Run(new Form1());
    }
}



1

(注意:这是一个有趣的解决方案!它可以工作,但是使用了错误的GDI +设计来实现这一点。)

将图像与您的应用程序一起放入并在启动时加载。按住它,直到应用程序退出。用户将无法启动第二个实例。(当然,互斥锁解决方案要干净得多)

private static Bitmap randomName = new Bitmap("my_image.jpg");

实际上,它的简单性非常出色,它几乎可以处理任何类型的文件,而不仅仅是图像。我觉得Mutex解决方案远非“干净”。它非常复杂,并且由于不正确,它显然有很多失败的方法。它还需要一种Main()与WPF应该如何工作相反的方法。
凯尔·德莱尼

这有点像使用bug。它可以工作,但并非出于此目的。我不会用它作为专业人士。
Bitterblue

是的,不幸的是,我们没有依赖异常的解决方案,这种有效而简单的解决方案。
凯尔·德莱尼

虽然这不是一个错误。.NET仍按预期运行。
凯尔·德莱尼

1
[STAThread]
static void Main()                  // args are OK here, of course
{
    bool ok;
    m = new System.Threading.Mutex(true, "YourNameHere", out ok);

    if (! ok)
    {
        MessageBox.Show("Another instance is already running.");
        return;
    }

    Application.Run(new Form1());   // or whatever was there

    GC.KeepAlive(m);                // important!
}

来自:确保.NET应用程序的单个实例

和:单实例应用程序互斥

与@Smink和@Imjustpondering相同的答案略有不同:

乔恩·斯凯特(Jon Skeet)在C#上的常见问题解答,以找出GC.KeepAlive为什么重要


-1,因为您无法在互斥锁上使用using块,这会使KeepAlive变得多余。是的,我确实认为John Skeet弄错了这一点。他没有详细说明为什么在这种情况下处理互斥锁是错误的。

1

只需使用StreamWriter,怎么样?

System.IO.File.StreamWriter OpenFlag = null;   //globally

try
{
    OpenFlag = new StreamWriter(Path.GetTempPath() + "OpenedIfRunning");
}
catch (System.IO.IOException) //file in use
{
    Environment.Exit(0);
}


0

这在纯C#中对我有用。try / catch是循环中列表中的某个进程可能退出的时间。

using System.Diagnostics;
....
[STAThread]
static void Main()
{
...
        int procCount = 0;
        foreach (Process pp in Process.GetProcesses())
        {
            try
            {
                if (String.Compare(pp.MainModule.FileName, Application.ExecutablePath, true) == 0)
                {
                    procCount++;                        
                    if(procCount > 1) {
                       Application.Exit();
                       return;
                    }
                }
            }
            catch { }
        }
        Application.Run(new Form1());
}

0

将应用程序限制为单个实例时,请务必考虑安全性:

全文:https : //blogs.msdn.microsoft.com/oldnewthing/20060620-13/?p=30813

我们正在使用具有固定名称的已命名互斥锁,以检测程序的另一个副本是否正在运行。但这也意味着攻击者可以首先创建互斥体,从而完全阻止我们的程序运行!如何防止这种拒绝服务攻击?

...

如果攻击者与程序正在(或将要在其中)运行在相同的安全上下文中,那么您将无能为力。无论您想出什么“秘密握手”来确定程序的另一个副本是否正在运行,攻击者都可以模仿它。由于它在正确的安全上下文中运行,因此它可以执行“真实”程序可以执行的任何操作。

...

显然,您无法保护自己免受以相同安全特权运行的攻击者的侵害,但仍可以保护自己免受以其他安全特权运行的非特权攻击者的攻击。

尝试在互斥锁上设置DACL,这是.NET方式:https : //msdn.microsoft.com/zh-cn/library/system.security.accesscontrol.mutexsecurity(v=vs.110).aspx


0

这些答案对我都不起作用,因为我需要使用monodevelop在Linux下工作。这对我很有用:

调用此方法并为其传递唯一ID

    public static void PreventMultipleInstance(string applicationId)
    {
        // Under Windows this is:
        //      C:\Users\SomeUser\AppData\Local\Temp\ 
        // Linux this is:
        //      /tmp/
        var temporaryDirectory = Path.GetTempPath();

        // Application ID (Make sure this guid is different accross your different applications!
        var applicationGuid = applicationId + ".process-lock";

        // file that will serve as our lock
        var fileFulePath = Path.Combine(temporaryDirectory, applicationGuid);

        try
        {
            // Prevents other processes from reading from or writing to this file
            var _InstanceLock = new FileStream(fileFulePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
            _InstanceLock.Lock(0, 0);
            MonoApp.Logger.LogToDisk(LogType.Notification, "04ZH-EQP0", "Aquired Lock", fileFulePath);

            // todo investigate why we need a reference to file stream. Without this GC releases the lock!
            System.Timers.Timer t = new System.Timers.Timer()
            {
                Interval = 500000,
                Enabled = true,
            };
            t.Elapsed += (a, b) =>
            {
                try
                {
                    _InstanceLock.Lock(0, 0);
                }
                catch
                {
                    MonoApp.Logger.Log(LogType.Error, "AOI7-QMCT", "Unable to lock file");
                }
            };
            t.Start();

        }
        catch
        {
            // Terminate application because another instance with this ID is running
            Environment.Exit(102534); 
        }
    }         
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.