【问题标题】:Detecting if another instance of the application is already running检测应用程序的另一个实例是否已在运行
【发布时间】:2011-03-12 12:54:34
【问题描述】:

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

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

例如:

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

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

(C#,桌面应用程序)

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

【问题讨论】:

  • 在您提到的场景中,期望的行为是什么?实例 3 应该做实例 1 正在做的事情,而实例 2 应该继续像以前一样吗? (假设您不想确保单个实例,因为这是由互斥锁解决的。实例 2 最终将退出)
  • 在这种情况下,实例 1 以一种方式运行,实例 2 和 3 需要使用替代行为。如果有一个实例已经在运行,无论它何时启动或此后发生了什么,一个新实例的行为都会略有不同——尽管不是很明显——不同。区别只是加载时的一次性操作。

标签: c# visual-studio-2008 mutex multiple-instances


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    另一种方法是检测正在运行的实例,详见Scott Hanselman's blog

    他的示例在第二个实例尝试时激活第一个实例。

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

    【讨论】:

    • 为什么没有人使用WindowsFormsApplicationBase中的现有机制?仅仅因为它的命名空间包含 VisualBasic!如果作者将它放在命名空间 Foo 中,它将被更多人使用。
    【解决方案3】:

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

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

    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;
    

    【讨论】:

    • 我要补充一点,APPLICATION-TITLE 是不带扩展名的可执行文件名(如果我没记错的话)。
    • @sztomi:感谢您激发了让代码自动确定的记忆。
    • 哈哈,不错。我想当我遇到这个问题时我硬编码了它。这好多了:)
    • 是的,做得很好。完美。
    【解决方案4】:

    您可以在使用CreateMutex() 创建互斥锁后简单地检查GetLastError() 吗?如果它返回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 标志 http://msdn.microsoft.com/en-us/library/bwe34f1k%28VS.80%29.aspx 的 Mutex 构造函数:

    public Mutex (
        bool initiallyOwned,
        string name,
        out bool createdNew
    )
    

    【讨论】:

      【解决方案5】:

      一个好的方法是使用 Sandor 解决方案,但使用 WMI 来获取进程列表,如下所述:C#: How to get the full path of running process?(Jeff 的解决方案)。这样,您还可以通过路径和远程终端会话 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;
              }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-06-04
        • 1970-01-01
        • 2017-01-02
        • 2010-10-02
        • 1970-01-01
        • 2014-03-26
        • 1970-01-01
        相关资源
        最近更新 更多