如何实现每台机器应用程序的单实例?

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

有没有办法创建一个机器范围的互斥体,还是有任何其他解决方案来实现每个机器应用程序的单个实例?

我会使用全局Mutex对象来执行此操作,该对象必须在应用程序的生命周期内保留。

 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 。 如果您省略了互斥锁的全局部分或将其替换为本地,那么互斥锁将仅针对每个会话(这可能是您当前代码的工作原理)。

我相信我首先从Jon Skeet那里获得了这项技术。

Mutex对象上的MSDN主题解释了Mutex对象的两个范围,并重点介绍了在使用终端服务时这一点很重要的原因(请参见倒数第二个)。

我认为您需要做的是使用系统sempahore来跟踪应用程序的实例。

如果使用接受名称的构造函数创建Semaphore对象,则它与该名称的操作系统信号量相关联。

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

编辑:请注意,我不知道这种方法是否适用于计算机上的多个Windows会话。 我认为它应该作为一个操作系统级别的构造,但我不能肯定地说,因为我没有那样测试它。

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

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

如何使用注册表?

  1. 您可以registry entry under HKEY_LOCAL_MACHINE创建一个registry entry under HKEY_LOCAL_MACHINE
  2. 如果应用程序启动,则将值设为flag
  3. 使用一些标准的symmetric key encryption方法symmetric key encryption以便其他任何人都无法篡改该值。
  4. On application start-up check for the key和中止\继续相应。
  5. 不要忘记obfuscate your assembly ,它执行这个加密\解密部分,这样就没有人可以通过查看reflection器中的代码来破解注册表中的密钥。

我曾做过类似的事情。

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

当然,这不是防弹,因为如果另一个应用程序具有完全相同的进程名称,您的应用程序将永远不会启动,但如果您使用非通用名称,它可能会非常好。

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

请注意,我还没有测试过它。

有关如何在WPF 3.5中完成单个instace应用程序的完整示例,请参阅下文

 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示例完整解决方案。

摘自“Pro WPF in C#3 2008,Apress,Matthew MacDonald”,买这本书就是黄金。 我做到了。