C# 如何确定我的应用程序的先前实例是否正在运行?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/778817/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to determine if a previous instance of my application is running?
提问by Jose Basilio
I have a console application in C# in which I run various arcane automation tasks. I am well aware that this should really be a Windows Servicesince it needs to run continuously, but I don'twant to do that at this time. (So, don't suggest that as an answer).
我有一个 C# 控制台应用程序,我在其中运行各种神秘的自动化任务。我很清楚这应该是一个Windows 服务,因为它需要连续运行,但我现在不想这样做。(因此,不要建议将其作为答案)。
In the meantime, I need some sample C# code that will allow me to determine if there's already an instance of the Application running.
同时,我需要一些示例 C# 代码来确定是否已经有应用程序的实例在运行。
In the old VB6.0 days, I would have used App.PrevInstance()
在旧的 VB6.0 时代,我会使用 App.PrevInstance()
I want to be able to do this in my Main method:
我希望能够在我的 Main 方法中做到这一点:
static void Main()
{
if(!MyApp.IsAlreadyRunning())
{
while(true)
{
RockAndRollAllNightAndPartyEveryDay();
}
}
}
采纳答案by Ian
Jeroen already answered this, but the best way by far is to use a Mutex... not by Process. Here's a fuller answer with code.
Jeroen 已经回答了这个问题,但到目前为止最好的方法是使用互斥锁......而不是通过进程。这是一个更完整的代码答案。
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");
}
Also bear in mind that you need to run your Application in some sort of Try{} Finally{} block. Otherwise if you're application crashes without releasing the Mutex then you may not be able to restart it again later.
另请记住,您需要在某种 Try{}Finally{} 块中运行您的应用程序。否则,如果您的应用程序在没有释放互斥锁的情况下崩溃,那么您以后可能无法再次重新启动它。
回答by Andy Mikula
You can use Process.GetProcessesByName("MyProcessName");
in the System.Diagnostics
namespace to check if there is an instance of your process running.
您可以Process.GetProcessesByName("MyProcessName");
在System.Diagnostics
命名空间中使用来检查是否有您的进程正在运行的实例。
EDIT: Very good observations in the comments! This is a (very) simplistic way of doing it, and certainly doesn't cover all the bases.
编辑:评论中的观察非常好!这是一种(非常)简单的方法,当然不能涵盖所有基础。
回答by Reed Copsey
You can search process names of existing system process. For example code, see this blog post.
您可以搜索现有系统进程的进程名称。例如代码,请参阅此博客文章。
You can also used a named system Mutex to see if your process is already running.
Here is some sample code.This tends to be more reliable in my experience, and is much simpler, more understandable code.
您还可以使用命名系统互斥锁来查看您的进程是否已经在运行。
这是一些示例代码。根据我的经验,这往往更可靠,并且是更简单、更易于理解的代码。
回答by Jeroen Landheer
The most simple (and reliable) way to do this, is using a Mutex. Use the WaitOne method of the Mutex class to wait until the mutex becomes available. An added advantage, this will not require any infinite loops
最简单(且可靠)的方法是使用互斥锁。使用 Mutex 类的 WaitOne 方法等待互斥锁可用。一个额外的优势,这不需要任何无限循环
回答by Ron
This article talks about it: Prevent a second process instance from running. It's in VB.net but you can convert it.
本文讨论它:防止第二个流程实例运行。它在 VB.net 中,但您可以转换它。
The problem in writing a generic function that checks whether the current application is already running comes from the fact that the ProcessName property of the Process object seems to be limited to 15 characters, so longer process names are truncated.
编写用于检查当前应用程序是否已在运行的通用函数的问题在于,Process 对象的ProcessName 属性似乎被限制为 15 个字符,因此会截断较长的进程名称。
A safer wayto retrieve a process name is to get the filename of its main module and dropping the extension. The following reusable routine uses this approach:
检索进程名称的更安全方法是获取其主模块的文件名并删除扩展名。以下可重用例程使用此方法:
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
回答by Mahtar
In one of my projects I used SingleInstance Component
在我的一个项目中,我使用了SingleInstance 组件
回答by Suneet
// 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;
}
Gracefully exit application with messagebox as shown above if application is already running
如果应用程序已经在运行,请使用消息框正常退出应用程序,如上所示
回答by Clinton Pierce
Another way to do it is to bind to an address on the local machine (as a TCP listener would). Only one process at a time can bind to a port/address combination. So pick a port on the loopback adapter and have at it.
另一种方法是绑定到本地机器上的地址(就像 TCP 侦听器一样)。一次只有一个进程可以绑定到一个端口/地址组合。因此,在环回适配器上选择一个端口并使用它。
This has the nice side-effects of:
这有很好的副作用:
- Working even if someone renames the executable
- Resetting itself when the application crashes
- The technique is portable across other operating systems
- 即使有人重命名可执行文件也能工作
- 应用程序崩溃时重置自身
- 该技术可移植到其他操作系统
On the down-side, it can fail if there's another application that binds to that particular port.
不利的一面是,如果有另一个应用程序绑定到该特定端口,则它可能会失败。
As requested, some code to bind to a address/port is below. This is ripped out of something else. It is incomplete, but the necessary bits are here.
根据要求,下面是一些绑定到地址/端口的代码。这是从别的东西里扯出来的。它是不完整的,但必要的位在这里。
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);
As long as listener is not garbage collected (falls out of scope) or the program doesn't terminate: that port on that adapter is yours and only yours. If anything should happen to listener
then it becomes available for someone else to use. For purposes of a lock, you should probably have listener
be static somewhere.
只要侦听器没有被垃圾收集(超出范围)或程序没有终止:该适配器上的那个端口就是你的,并且只是你的。如果发生任何事情,listener
那么它就可以供其他人使用。出于锁定的目的,您可能应该listener
在某处保持静态。
回答by Jim In Texas
Using a kernal object is the only correct way to implement single instance protection in Windows.
使用内核对象是在 Windows 中实现单实例保护的唯一正确方法。
This statement:
这个说法:
mutex = Mutex.OpenExisting("SINGLEINSTANCE");
mutex = Mutex.OpenExisting("SINGLEINSTANCE");
won't work if someone else copies this line from Stackoverflow and runs their program before your program, since that other guy grabbed "SINGLEINSTANCE" before you did. You want to include a GUID in your mutex name:
如果其他人从 Stackoverflow 复制这一行并在您的程序之前运行他们的程序将无法工作,因为其他人在您之前抢到了“SINGLEINSTANCE”。您想在互斥锁名称中包含 GUID:
mutex = Mutex.OpenExisting("MyApp{AD52DAF0-C3CF-4cc7-9EDD-03812F82557E}");
mutex = Mutex.OpenExisting("MyApp{AD52DAF0-C3CF-4cc7-9EDD-03812F82557E}");
This technique will prevent the current user from running more than one instance of your program, but will not prevent another user from doing so.
这种技术将阻止当前用户运行多个程序实例,但不会阻止另一用户这样做。
To ensure that only one instance of your application can run on the local computer, you need to do this:
为确保只有一个应用程序实例可以在本地计算机上运行,您需要执行以下操作:
mutex = Mutex.OpenExisting("Global\MyApp{AD52DAF0-C3CF-4cc7-9EDD-03812F82557E}");
mutex = Mutex.OpenExisting("Global\MyApp{AD52DAF0-C3CF-4cc7-9EDD-03812F82557E}");
See the help for the CreateMutex api.
请参阅 CreateMutex api 的帮助。
回答by Roman Starkov
The proper way to use a mutex for this purpose:
为此目的使用互斥锁的正确方法:
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
}
The above won't work for detecting if several users on the same machine are running the app under different user accounts. A similar case is where a process can run bothunder the service host andstandalone. To make these work, create the mutex as follows:
以上不适用于检测同一台机器上的多个用户是否在不同的用户帐户下运行应用程序。类似的情况是一个过程可以运行两个服务主机下和独立。要使这些工作,请按如下方式创建互斥锁:
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);
Two differences here - firstly, the mutex needs to be created with security rights that allow other user accounts to open/acquire it. Second, the name mustbe prefixed with "Global" in the case of services running under the service host (not sure about other users running locally on the same machine).
这里有两个不同之处 - 首先,需要使用安全权限创建互斥锁,以允许其他用户帐户打开/获取它。其次,对于在服务主机下运行的服务(不确定在同一台机器上本地运行的其他用户),名称必须以“全局”为前缀。