137

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

4

22 回答 22

164

使用互斥锁。上面使用 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";
于 2008-09-18T16:38:19.543 回答
25
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;
}
于 2008-09-18T16:16:04.400 回答
23

这是确保只有一个实例在运行所需的代码。这是使用命名互斥锁的方法。

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();
        }
    }
}
于 2008-09-18T18:26:18.723 回答
7

Hanselman 有一篇关于使用 Microsoft.VisualBasic 程序集中的 WinFormsApplicationBase 类来执行此操作的帖子。

于 2008-09-18T16:11:35.067 回答
7

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

using System.Diagnostics;

void Main()2 -作为第一行代码放入->

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

就是这样。

于 2015-09-01T16:25:18.060 回答
5

听起来到目前为止已经提出了 3 种基本技术。

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

我错过了任何警告吗?

于 2008-09-18T17:32:22.563 回答
5

我在这里尝试了所有解决方案,但在我的 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
}

我只需要在某些条件下关闭其他 istances,这对我的目的很有效

于 2014-02-06T09:00:16.837 回答
5

在尝试了多种解决方案后,我的问题。我最终在这里使用了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"
于 2015-08-05T08:47:17.383 回答
4

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

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

于 2008-09-18T16:22:36.007 回答
3

http://en.csharp-online.net/Application_Architecture_in_Windows_Forms_2.0 —Single-Instance_Detection_and_Management

于 2008-09-18T16:10:45.053 回答
3

这是 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());
    }
}
于 2012-10-31T07:23:00.493 回答
2

本文简单地解释了如何创建一个可以控制其实例数量或仅运行单个实例的 Windows 应用程序。这是业务应用程序非常典型的需求。已经有很多其他可能的解决方案来控制这一点。

https://web.archive.org/web/20090205153420/http://www.openwinforms.com/single_instance_application.html

于 2008-09-18T16:11:29.560 回答
2

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

使用 Microsoft.VisualBasic.ApplicationServices;

VB.Net 的 WindowsFormsApplicationBase 为您提供了一个“SingleInstace”属性,它决定了其他实例并只让一个实例运行。

于 2008-09-18T19:19:49.890 回答
2
[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为何如此重要

于 2009-02-04T02:31:30.053 回答
1

http://www.codeproject.com/KB/cs/SingleInstancingWithIpc.aspx

于 2008-09-18T16:11:43.133 回答
1

您必须使用 System.Diagnostics.Process。

查看: http: //www.devx.com/tips/Tip/20044

于 2008-09-18T16:11:48.640 回答
1

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

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

private static Bitmap randomName = new Bitmap("my_image.jpg");
于 2014-01-28T14:09:48.870 回答
1

简单地使用 a 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);
}
于 2017-01-11T17:59:57.043 回答
0

通常它是通过一个命名的 Mutex 完成的(使用 new Mutex("your app name", true ) 并检查返回值),但 Microsoft.VisualBasic.dll 中也有一些支持类可以为您完成

于 2008-09-18T16:11:58.187 回答
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());
}
于 2015-06-18T05:38:16.863 回答
0

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

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

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

...

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

...

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

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

于 2017-01-11T18:43:53.857 回答
0

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

调用此方法并传递一个唯一 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); 
        }
    }         
于 2019-02-11T23:24:27.187 回答