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

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

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

在旧的VB6.0天,我会使用App.PrevInstance()

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

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

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 {}块中运行您的应用程序。 否则,如果您在没有释放互斥锁的情况下发生应用程序崩溃,那么您可能无法在以后重新启动它。

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

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

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

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

您可以搜索现有系统进程的进程名称。 例如代码, 请参阅此博客文章。

您还可以使用命名系统Mutex来查看您的进程是否已在运行。
这是一些示例代码。 根据我的经验,这往往更可靠,并且代码更简单,更容易理解。

本文讨论它: 防止第二个流程实例运行 。 它在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 

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

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

  // 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正常退出应用程序,如上所示

使用内核对象是在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的帮助。

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

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

这有很好的副作用:

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

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


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

 using System.Net; using System.Net.Sockets; [...] // Make up a number that's currently unused by you, or any // well-known service. ie 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在某个地方保持静态。