如何实现单实例单机应用?

发布于 2024-10-03 11:27:18 字数 168 浏览 5 评论 0原文

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

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

I have to restrict my .net 4 WPF application so that it can be run only once per machine. Note that I said per machine, not per session.
I implemented single instance applications using a simple mutex until now, but unfortunately such a mutex is per session.

Is there a way to create a machine wide mutex or is there any other solution to implement a single instance per machine application?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

等你爱我 2024-10-10 11:27:18

我将使用必须在应用程序的整个生命周期中保留的全局互斥对象来执行此操作。

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 主题解释了这两个对象互斥对象的范围,并强调为什么这在使用终端服务时很重要(请参阅倒数第二个注释)。

I would do this with a global Mutex object that must be kept for the life of your application.

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

Where bFirstInstance returns if this is the first instance of your application running globally. If you omited the Global part of the mutex or replaced it with Local then the mutex would only be per session (this is proberbly how your current code is working).

I believe that I got this technique first from Jon Skeet.

The MSDN topic on the Mutex object explains about the two scopes for a Mutex object and highlights why this is important when using terminal services (see second to last note).

绾颜 2024-10-10 11:27:18

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

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

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

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

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

I think what you need to do is use a system sempahore to track the instances of your application.

If you create a Semaphore object using a constructor that accepts a name, it is associated with an operating-system semaphore of that name.

Named system semaphores are visible throughout the operating system, and can be used to synchronize the activities of processes.

EDIT: Note that I am not aware if this approach works across multiple windows sessions on a machine. I think it should as its an OS level construct but I cant say for sure as i havent tested it that way.

EDIT 2: I did not know this but after reading Stevo2000's answer, i did some looking up as well and I think that the "Global\" prefixing to make the the object applicable to the global namespace would apply to semaphores as well and semaphore, if created this way, should work.

妳是的陽光 2024-10-10 11:27:18

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

You could open a file with exclusive rights somewhere in %PROGRAMDATA%
The second instance that starts will try to open the same file and fail if it's already open.

放我走吧 2024-10-10 11:27:18

使用注册表怎么样?

  1. 您可以在 HKEY_LOCAL_MACHINE 下创建一个注册表项
  2. 如果应用程序已启动,则将该值设为flag
  3. 使用某种标准对称密钥加密方法对密钥进行加密,以便其他人无法篡改该值。
  4. 在应用程序启动时检查密钥并相应地中止\继续。
  5. 不要忘记混淆您的程序集,它执行此加密\解密部分,这样就没有人可以通过查看反射器中的代码来破解注册表中的密钥。

How about using the registry?

  1. You can create a registry entry under HKEY_LOCAL_MACHINE.
  2. Let the value be the flag if the application is started or not.
  3. Encrypt the key using some standard symmetric key encryption method so that no one else can tamper with the value.
  4. On application start-up check for the key and abort\continue accordingly.
  5. Do not forget to obfuscate your assembly, which does this encryption\decryption part, so that no one can hack the key in registry by looking at the code in reflector.
拥醉 2024-10-10 11:27:18

我曾经做过类似的事情。

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

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

I did something similar once.

When staring up the application list, I checked all running processes for a process with identical name, and if it existed I would not allow to start the program.

This is not bulletproof of course, since if another application have the exact same process name, your application will never start, but if you use a non-generic name it will probably be more than good enough.

沉睡月亮 2024-10-10 11:27:18

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

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

For the sake of completeness, I'd like to add the following which I just found now:
This web site has an interesting approach in sending Win32 messages to other processes. This would fix the problem of the user renaming the assembly to bypass the test and of other assemblies with the same name.
They're using the message to activate the main window of the other process, but it seems like the message could be a dummy message only used to see whether the other process is responding to it to know whether it is our process or not.

Note that I haven't tested it yet.

滴情不沾 2024-10-10 11:27:18

只是想分享一种更简单的方法来做到这一点:)

bool firstInstance;
try
{
    using (var mutex = new Mutex(true, "Global\\mutexname", out firstInstance))
    {
        if (!firstInstance)
        {
            Console.WriteLine("Another instance of application is already running.");
            return;
        }

        Console.WriteLine("This is the first instance of the application.");
    }
}
catch (UnauthorizedAccessException ex)
{
    Console.WriteLine("Another instance of the application is already in use by another user.");
}

Just want to share a easier way to do this :)

bool firstInstance;
try
{
    using (var mutex = new Mutex(true, "Global\\mutexname", out firstInstance))
    {
        if (!firstInstance)
        {
            Console.WriteLine("Another instance of application is already running.");
            return;
        }

        Console.WriteLine("This is the first instance of the application.");
    }
}
catch (UnauthorizedAccessException ex)
{
    Console.WriteLine("Another instance of the application is already in use by another user.");
}
孤檠 2024-10-10 11:27:18

请参阅下面的完整示例,了解如何在 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);
    }
    }

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

您还可以从此处下载本书的源代码,下载 VS 示例完整解决方案。

摘自《Pro WPF in C#3 2008》,Apress,Matthew MacDonald,买书就是金币。我做到了。

See below for full example of how a single instace app is done in 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]);
}
}
}

Second part:

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

Third part:

 public class Startup
    {
    [STAThread]
    public static void Main(string[] args)
    {
    SingleInstanceApplicationWrapper wrapper =
    new SingleInstanceApplicationWrapper();
    wrapper.Run(args);
    }
    }

You may need to add soem references and add some using statements but it shoudl work.

You can also download a VS example complete solution by downloading the source code of the book from here.

Taken From "Pro WPF in C#3 2008 , Apress , Matthew MacDonald" , buy the book is gold. I did.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文