13

如果已经有一个实例正在运行,我的应用程序在加载时的行为需要稍有不同。

我了解如何使用互斥锁来防止加载额外的实例,但这并不能完全解决我的问题。

例如:

  • 实例 1 加载,获取互斥锁。
  • 实例 2 加载,无法获取互斥锁,知道还有另一个实例。到现在为止还挺好。
  • 实例 1 关闭,释放互斥锁。
  • 实例 3 加载,获取互斥锁,不知道实例 2 仍在运行。

有任何想法吗?值得庆幸的是,它不需要处理多个用户帐户或类似的东西。

(C#,桌面应用程序)

编辑:为了澄清,应用程序不需要限制为单个实例,如果有另一个实例已经在运行,只需执行稍微不同的启动操作。多个实例很好(并且是预期的)。

4

5 回答 5

12

这可能会做你想要的。它有一个很好的附加功能,可以将已经运行的实例向前推进。

编辑:更新代码以自动确定应用程序标题。

using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;

static void Main()
{
    if (!EnsureSingleInstance())
    {
        return;
    }

    //...
}

static bool EnsureSingleInstance()
{
    Process currentProcess = Process.GetCurrentProcess();

    var runningProcess = (from process in Process.GetProcesses()
                          where
                            process.Id != currentProcess.Id &&
                            process.ProcessName.Equals(
                              currentProcess.ProcessName,
                              StringComparison.Ordinal)
                          select process).FirstOrDefault();

    if (runningProcess != null)
    {
        ShowWindow(runningProcess.MainWindowHandle, SW_SHOWMAXIMIZED);
        SetForegroundWindow(runningProcess.MainWindowHandle);

        return false;
    }

    return true;
}

[DllImport("user32.dll", EntryPoint = "SetForegroundWindow")]
private static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("user32.dll")]
private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow);

private const int SW_SHOWMAXIMIZED = 3;
于 2010-07-06T21:07:58.023 回答
2

另一种方法是检测正在运行的实例,如Scott Hanselman 的博客中所述

当第二个实例尝试时,他的示例会激活第一个实例。

但是,如果这是您想要的,让第二个实例停止并不难。

于 2010-07-06T21:07:29.077 回答
1

尝试使用信号量而不是互斥量

于 2010-07-06T21:03:04.157 回答
0

GetLastError()您可以在创建互斥锁后简单地检查一下CreateMutex()吗?如果它返回ERROR_ALREADY_EXISTS,那么您的应用程序还有另一个正在运行的实例。

根据http://msdn.microsoft.com/en-us/library/ms682411%28VS.85%29.aspx

如果互斥锁是一个命名互斥锁并且该对象在此函数调用之前存在,则返回值是现有对象的句柄,GetLastError 返回 ERROR_ALREADY_EXISTS,bInitialOwner 被忽略,调用线程不被授予所有权。但是,如果调用者的访问权限有限,则该函数将失败并显示 ERROR_ACCESS_DENIED 并且调用者应使用 OpenMutex 函数。

编辑:刚刚意识到这是一个 C#/.Net 问题,抱歉。

在 .Net 中,使用返回 createdNew 标志的 Mutex 构造函数, http: //msdn.microsoft.com/en-us/library/bwe34f1k%28VS.80%29.aspx

public Mutex (
    bool initiallyOwned,
    string name,
    out bool createdNew
)
于 2010-07-06T21:48:19.033 回答
0

一个好的方法是使用 Sandor 解决方案,但使用 WMI 获取进程列表,如下所述:C#: How to get the full path of running process? (杰夫的解决方案)。这样,您还可以通过路径和远程终端会话 ID 检查其他正在运行的实例是否匹配:

    static bool EnsureSingleInstance()
    {
        Process currentProcess = Process.GetCurrentProcess();

        var wmiQueryString = "SELECT ProcessId, ExecutablePath, CommandLine FROM Win32_Process";
        using (var searcher = new ManagementObjectSearcher(wmiQueryString))
        using (var results = searcher.Get())
        {
            var query = from p in Process.GetProcesses()
                        join mo in results.Cast<ManagementObject>()
                        on p.Id equals (int)(uint)mo["ProcessId"]
                        select new
                        {
                            Process = p,
                            Path = (string)mo["ExecutablePath"],
                            CommandLine = (string)mo["CommandLine"],
                        };

            var runningProcess = (from process in query
                                  where
                                    process.Process.Id != currentProcess.Id &&
                                    process.Process.ProcessName.Equals(
                                      currentProcess.ProcessName,
                                      StringComparison.Ordinal) &&
                                      process.Path == currentProcess.MainModule.FileName &&
                                      process.Process.SessionId == currentProcess.SessionId
                                  select process).FirstOrDefault();

            return runningProcess == null;
        }
    }
于 2014-12-05T09:14:59.860 回答