10

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

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

4

7 回答 7

14

我会使用一个全局 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如果这是您的应用程序在全球范围内运行的第一个实例,则返回Where 。如果您省略了互斥锁的 Global 部分或将其替换为 Local 则互斥锁将仅适用于每个会话(这可能是您当前代码的工作方式)。

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

关于Mutex对象的 MSDN 主题解释了 Mutex 对象的两个作用域,并强调了为什么这在使用终端服务时很重要(参见倒数第二个注释)。

于 2010-11-19T08:47:08.300 回答
3

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

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

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

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

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

于 2010-11-19T08:00:02.300 回答
2

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

于 2010-11-19T07:59:05.593 回答
1

如何使用注册表?

  1. 您可以创建一个registry entry under HKEY_LOCAL_MACHINE.
  2. 让值是flag应用程序是否启动。
  3. Encrypt the key使用某种标准symmetric key encryption方法,以便其他人无法篡改该值。
  4. On application start-up check for the key并相应地中止\继续。
  5. 不要忘记obfuscate your assembly,它执行此加密\解密部分,因此没有人可以通过查看反射器中的代码来破解注册表中的密钥。
于 2010-11-19T08:27:47.260 回答
0

我曾经做过类似的事情。

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

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

于 2010-11-19T07:53:43.607 回答
0

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

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

于 2010-11-19T08:39:59.313 回答
-1

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

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

您可能需要添加一些引用并添加一些 using 语句,但它应该可以工作。

您也可以通过从这里下载本书的源代码来下载 VS 示例完整解决方案。

取自“Pro WPF in C#3 2008 , Apress , Matthew MacDonald”,买书是金子。我做到了。

于 2010-11-19T08:27:35.650 回答