C# 如何实现每台计算机应用程序一个实例?

C# 如何实现每台计算机应用程序一个实例?,c#,.net,mutex,single-instance,C#,.net,Mutex,Single Instance,我必须限制我的.NET4WPF应用程序,以便每台机器只能运行一次。请注意,我说的是每台机器,而不是每会话。 到目前为止,我使用一个简单的互斥体实现了单实例应用程序,但不幸的是,这样的互斥体是每个会话的 有没有一种方法可以创建机器范围的互斥,或者有没有其他解决方案可以实现每台机器应用程序一个实例?我曾经做过类似的事情 当启动应用程序列表时,我检查了所有正在运行的进程是否有相同名称的进程,如果它存在,我将不允许启动该程序 这当然不是绝对可靠的,因为如果另一个应用程序具有完全相同的进程名,您的应用程序

我必须限制我的.NET4WPF应用程序,以便每台机器只能运行一次。请注意,我说的是每台机器,而不是每会话。
到目前为止,我使用一个简单的互斥体实现了单实例应用程序,但不幸的是,这样的互斥体是每个会话的


有没有一种方法可以创建机器范围的互斥,或者有没有其他解决方案可以实现每台机器应用程序一个实例?

我曾经做过类似的事情

当启动应用程序列表时,我检查了所有正在运行的进程是否有相同名称的进程,如果它存在,我将不允许启动该程序


这当然不是绝对可靠的,因为如果另一个应用程序具有完全相同的进程名,您的应用程序将永远不会启动,但如果您使用非通用名称,它可能已经足够好了。

您可以在%PROGRAMDATA%中的某个位置打开具有独占权限的文件
启动的第二个实例将尝试打开同一个文件,如果该文件已经打开,则会失败。

我认为您需要做的是使用跟踪应用程序的实例

MutexSecurity oMutexSecurity;

//Set the security object
oMutexSecurity = new MutexSecurity();
oMutexSecurity.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null), MutexRights.FullControl, AccessControlType.Allow));

//Create the global mutex and set its security
moGlobalMutex = new Mutex(True, "Global\\{5076d41c-a40a-4f4d-9eed-bf274a5bedcb}", bFirstInstance);
moGlobalMutex.SetAccessControl(oMutexSecurity);
如果使用接受名称的构造函数创建信号量对象,则该对象与该名称的操作系统信号量相关联

命名系统信号量在整个操作系统中可见,可用于同步进程的活动

编辑:请注意,我不知道这种方法是否适用于机器上的多个windows会话。我认为它应该是一个操作系统级的结构,但我不能肯定,因为我还没有这样测试过它


编辑2:我不知道这一点,但在阅读Stevo2000的答案后,我也进行了一些查找,我认为使对象适用于全局名称空间的“Global\”前缀也适用于信号量和信号量,如果以这种方式创建,应该可以工作。

请参阅下面的完整示例,了解如何在WPF 3.5中实现单个Instance应用程序

public class SingleInstanceApplicationWrapper :
Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
{
public SingleInstanceApplicationWrapper()
{
// Enable single-instance mode.
this.IsSingleInstance = true;
}
// Create the WPF application class.
private WpfApp app;
protected override bool OnStartup(
Microsoft.VisualBasic.ApplicationServices.StartupEventArgs e)
{
app = new WpfApp();
app.Run();
return false;
}
// Direct multiple instances.
protected override void OnStartupNextInstance(
Microsoft.VisualBasic.ApplicationServices.StartupNextInstanceEventArgs e)
{
if (e.CommandLine.Count > 0)
{
app.ShowDocument(e.CommandLine[0]);
}
}
}
第二部分:

public class WpfApp : System.Windows.Application
{
protected override void OnStartup(System.Windows.StartupEventArgs e)
{
base.OnStartup(e);
WpfApp.current = this;
// Load the main window.
DocumentList list = new DocumentList();
this.MainWindow = list;
list.Show();
// Load the document that was specified as an argument.
if (e.Args.Length > 0) ShowDocument(e.Args[0]);
}
public void ShowDocument(string filename)
{
try
{
Document doc = new Document();
doc.LoadFile(filename);
doc.Owner = this.MainWindow;
doc.Show();
// If the application is already loaded, it may not be visible.
// This attempts to give focus to the new window.
doc.Activate();
}
catch
{
MessageBox.Show("Could not load document.");
}
}
}
第三部分:

 public class Startup
    {
    [STAThread]
    public static void Main(string[] args)
    {
    SingleInstanceApplicationWrapper wrapper =
    new SingleInstanceApplicationWrapper();
    wrapper.Run(args);
    }
    }
您可能需要添加soem引用和一些using语句,但它应该可以工作

您还可以通过从下载本书的源代码下载VS示例完整解决方案


摘自《2008年3月职业WPF》一书,按马修·麦克唐纳的说法,《买书是黄金》。是的。

使用注册表怎么样

  • 您可以在HKEY_LOCAL_机器下创建
    注册表项
  • 如果应用程序是否启动,则将该值设为
    标志
  • 使用一些标准的
    对称密钥加密
    方法加密密钥
    ,这样其他人就不能篡改该值
  • 应用程序启动时检查密钥
    并中止\继续相应操作
  • 不要忘记对程序集进行模糊处理,该程序集执行此加密/解密部分,因此没有人可以通过查看reflector中的代码来破解注册表中的密钥

  • 为了完整起见,我想添加我刚刚发现的以下内容:
    这是一种将Win32消息发送到其他进程的有趣方法。这将解决用户重命名程序集以绕过测试和具有相同名称的其他程序集的问题。
    他们正在使用该消息来激活另一进程的主窗口,但似乎该消息可能是一条虚拟消息,仅用于查看另一进程是否响应该消息,以了解它是否是我们的进程


    请注意,我还没有对它进行测试。

    我将使用一个全局互斥对象来进行测试,该对象必须在应用程序的生命周期内保留

    MutexSecurity oMutexSecurity;
    
    //Set the security object
    oMutexSecurity = new MutexSecurity();
    oMutexSecurity.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null), MutexRights.FullControl, AccessControlType.Allow));
    
    //Create the global mutex and set its security
    moGlobalMutex = new Mutex(True, "Global\\{5076d41c-a40a-4f4d-9eed-bf274a5bedcb}", bFirstInstance);
    moGlobalMutex.SetAccessControl(oMutexSecurity);
    
    其中
    bFirstInstance
    如果这是第一个全局运行的应用程序实例,则返回。如果省略了互斥体的全局部分或将其替换为本地部分,则互斥体将仅在每个会话中使用(这可能是当前代码的工作方式)

    我相信这项技术是我从一开始就学会的


    对象上的MSDN主题解释了互斥对象的两个作用域,并强调了在使用终端服务时这一点的重要性(请参见倒数第二条)。

    会话指的是Windows会话?是的,Windows会话。因此必须允许它在每台机器上运行一次,而不是每个用户。实际上,
    Mutex
    可以帮助每个会话实现单个实例,而不是每个会话。这里最好的问题是为什么?最常见的原因是应用程序使用某种独特的资源。通常,最好的答案是与该独特资源直接相关的答案。例如,当唯一资源是一个文件时,答案是以独占方式锁定该文件。问题是用户只需重命名程序集即可。但事实上,如果没有其他解决方案,那么我将使用这种技术。也许有些事情可以通过WCF渠道来完成……在有限帐户下运行的程序可以看到其他帐户的进程吗?是的,我只是通过创建一个用户来测试它,这个用户只是“用户”组的一部分,这个用户可以看到其他人的进程。只有一些属性会生成拒绝访问异常,但至少可以使用进程名。这听起来是个好主意。我曾考虑过创建/删除一个锁文件,但这会导致访问权限问题。但简单地打开/关闭一个文件就可以做到这一点。谢谢你的主意!听起来是个很有趣的主意,所以我试了一下。不幸的是,它与互斥类相同,另一个用户可以安全地创建具有相同名称的信号量。注意,我没有使用以信号量安全对象为参数的构造函数。也许可以通过这个实现一些东西?嗯……也许我没有完全正确地理解您的需求,但是在您的应用程序中——当它启动时,首先您使用OpenExisting(“MyAppSemaphore”)检查是否存在一个名为“MyAppSemaphore”的信号量。如果存在,则关闭应用程序。如果没有,