如何确定我的应用程序的先前实例是否正在运行?

时间:2009-04-22 19:32:56

标签: c# .net

我在C#中有一个控制台应用程序,我在其中运行各种神秘的自动化任务。我很清楚这应该是一个 Windows服务,因为它需要连续运行,但我想要这样做。 (所以,不要建议作为答案)。

与此同时,我需要一些示例C#代码,这些代码将允许我确定是否已经运行了应用程序的实例。

在旧版VB6.0中,我会使用App.PrevInstance()

我希望能够在我的Main方法中执行此操作:

static void Main()
{
  if(!MyApp.IsAlreadyRunning())
  {
    while(true)
    {
      RockAndRollAllNightAndPartyEveryDay();
    }
  }
}

10 个答案:

答案 0 :(得分:24)

为此目的使用互斥锁的正确方法:

private static Mutex mutex;

static void Main()
{
    // STEP 1: Create and/or check mutex existence in a race-free way
    bool created;
    mutex = new Mutex(false, "YourAppName-{add-your-random-chars}", out created);
    if (!created)
    {
        MessageBox.Show("Another instance of this application is already running");
        return;
    }

    // STEP 2: Run whatever the app needs to do
    Application.Run(new Form1());

    // No need to release the mutex because it was never acquired
}

以上内容不适用于检测同一台计算机上的多个用户是否在不同的用户帐户下运行该应用程序。类似的情况是进程可以在服务主机独立下运行两者。要使这些工作,请按如下方式创建互斥锁:

        var sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
        var mutexsecurity = new MutexSecurity();
        mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.FullControl, AccessControlType.Allow));
        mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.ChangePermissions, AccessControlType.Deny));
        mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.Delete, AccessControlType.Deny));
        _mutex = new Mutex(false, "Global\\YourAppName-{add-your-random-chars}", out created, mutexsecurity);

这里有两个不同之处 - 首先,需要使用允许其他用户帐户打开/获取它的安全权限来创建互斥锁。其次,在服务主机下运行的服务的情况下,名称​​必须以“全局”为前缀(不确定在同一台机器上本地运行的其他用户)。

答案 1 :(得分:18)

Jeroen已经回答了这个问题,但到目前为止最好的方法是使用Mutex ......而不是使用Process。这是一个更完整的代码答案。

Mutex mutex;

try
{
   mutex = Mutex.OpenExisting("SINGLEINSTANCE");
   if (mutex!= null)
   {
      Console.WriteLine("Error : Only 1 instance of this application can run at a time");
      Application.Exit();
   }
}
catch (WaitHandleCannotBeOpenedException ex)
{
   mutex  = new Mutex(true, "SINGLEINSTANCE");
}

另请注意,您需要在某种Try {} Finally {}块中运行您的应用程序。否则,如果你的应用程序在没有释放互斥锁的情况下崩溃,那么你可能以后无法重新启动它。

答案 2 :(得分:4)

最简单(也是最可靠)的方法是使用Mutex。使用Mutex类的WaitOne方法等待互斥锁变为可用。一个额外的优势,这不需要任何无限循环

答案 3 :(得分:2)

您可以搜索现有系统进程的进程名称。例如代码see this blog post.

您还可以使用命名系统Mutex查看您的进程是否已在运行 Here is some sample code.这在我的经验中往往更可靠,并且代码更简单,更容易理解。

答案 4 :(得分:2)

本文讨论它:Prevent a second process instance from running。它在VB.net中,但你可以转换它。

编写检查当前应用程序是否已在运行的泛型函数的问题来自于Process对象的 ProcessName属性似乎限制为15个字符,因此更长的进程名称被截断。
更安全的方式来检索进程名称是获取其主模块的文件名并删除扩展名。以下可重用例程使用此方法:

Function AppIsAlreadyRunning() As Boolean
    ' get the filename of the main module
    Dim moduleName As String = Process.GetCurrentProcess.MainModule.ModuleName

    ' discard the extension to get the process name
    Dim procName As String = System.IO.Path.GetFileNameWithoutExtension(moduleName)

    ' return true if there are 2 or more processes with that name
    If Process.GetProcessesByName(procName).Length > 1 Then
        Return True
    End If
End Function

答案 5 :(得分:2)

    // Allow running single instance
    string processName = Process.GetCurrentProcess().ProcessName;
    Process[] instances = Process.GetProcessesByName(processName);

    if (instances.Length > 1)
    {
        MessageBox.Show("Application already Running", "Error 1001 - Application Running");
        return;
    }

如果应用程序已在运行

,请使用messagebox正常退出应用程序,如上所示

答案 6 :(得分:1)

您可以在Process.GetProcessesByName("MyProcessName");命名空间中使用System.Diagnostics来检查您的流程是否正在运行。

编辑:评论中非常好的观察!这是一种(非常)简单的方式,当然也没有涵盖所有基础。

答案 7 :(得分:1)

使用内核对象是在Windows中实现单实例保护的唯一正确方法。

本声明:

mutex = Mutex.OpenExisting(“SINGLEINSTANCE”);

如果其他人从Stackoverflow复制这一行并在你的程序之前运行他们的程序,

将无法工作,因为其他人在你之前抓住了“SINGLEINSTANCE”。您希望在互斥锁名称中包含GUID:

mutex = Mutex.OpenExisting(“MyApp {AD52DAF0-C3CF-4cc7-9EDD-03812F82557E}”);

此技术将阻止当前用户运行多个程序实例,但不会阻止其他用户这样做。

要确保只有一个应用程序实例可以在本地计算机上运行,​​您需要执行以下操作:

mutex = Mutex.OpenExisting(“Global \ MyApp {AD52DAF0-C3CF-4cc7-9EDD-03812F82557E}”);

请参阅CreateMutex api的帮助。

答案 8 :(得分:0)

在我的一个项目中,我使用了SingleInstance Component

答案 9 :(得分:-3)

另一种方法是绑定到本地计算机上的地址(作为TCP侦听器)。一次只能有一个进程绑定到端口/地址组合。因此,在环回适配器上选择一个端口,然后使用它。

这有很好的副作用:

  • 即使有人重命名可执行文件
  • 也能正常工作
  • 应用程序崩溃时重置自身
  • 该技术可跨其他操作系统移植

在不利方面,如果有另一个应用程序绑定到该特定端口,它可能会失败。


根据要求,绑定到地址/端口的一些代码如下所示。这被其他东西撕掉了。它不完整,但必要的位在这里。

using System.Net;
using System.Net.Sockets;

[...]

// Make up a number that's currently unused by you, or any 
// well-known service. i.e. 80 for http, 22 for ssh, etc..
int portNum = 2001;  

// This binds to any (meaning all) adapters on this system 
IPAddress ipAddress = IPAddress.Any;
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, portNum);
Socket listener = new Socket(AddressFamily.InterNetwork,
    SocketType.Stream, ProtocolType.Tcp );

// The next statement will throw an exception if anyone has done this Bind!
listener.Bind(localEndPoint);

只要监听器不是垃圾收集(超出范围)或程序没有终止:该适配器上的端口是你的,只有你自己的端口。如果listener发生任何事情,那么其他人可以使用它。出于锁定的目的,您可能应该listener在某处静止。

相关问题