如何强制C# .net应用程序在Windows中只运行一个实例?

77
4个回答

122

我更喜欢以下类似的互斥锁解决方案。因为这样,如果应用程序已经加载,它会重新聚焦于该应用程序。

using System.Threading;

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
   bool createdNew = true;
   using (Mutex mutex = new Mutex(true, "MyApplicationName", out createdNew))
   {
      if (createdNew)
      {
         Application.EnableVisualStyles();
         Application.SetCompatibleTextRenderingDefault(false);
         Application.Run(new MainForm());
      }
      else
      {
         Process current = Process.GetCurrentProcess();
         foreach (Process process in Process.GetProcessesByName(current.ProcessName))
         {
            if (process.Id != current.Id)
            {
               SetForegroundWindow(process.MainWindowHandle);
               break;
            }
         }
      }
   }
}

10
关于使用MainWindowHandle属性的一些小提示(我刚刚发现):如果相关进程没有主窗口,那么MainWindowHandle值为零。对于已隐藏的进程,即不在任务栏中可见的进程,该值也为零。这可能适用于在任务栏最右侧出现的通知区域图标的进程。 - Nick Spreitzer
3
SetForegroundWindow 对我来说不起作用,实际上从来没有起作用过。 - jay_t55
运行得很好。不过有一个问题:"MyApplicationName" 应该是程序集的名称、解决方案/项目的名称还是 GUI 窗口的标题?似乎两种方式都可以工作(使用 "MyApplicationName" 也可以)。 - user1021726
1
@user1021726,“MyApplicationName” 应该是对你来说唯一的。我建议使用有意义的东西,后面跟着一个 GUID。 - davidm_uk
4
如果您的应用可以最小化,您可能还想调用ShowWindow(process.MainWindowHandle, SW_RESTORE)来将其恢复。 https://dev59.com/k2455IYBdhLWcg3wD_sB 提供了详细信息。SW_RESTORE是9(来自http://msdn.microsoft.com/en-gb/library/windows/desktop/ms633548%28v=vs.85%29.aspx)。 - davidm_uk
显示剩余5条评论

33

如果想要在 .net (C#) 中强制运行程序的唯一实例,可以在 program.cs 文件中使用以下代码:

public static Process PriorProcess()
    // Returns a System.Diagnostics.Process pointing to
    // a pre-existing process with the same name as the
    // current one, if any; or null if the current process
    // is unique.
    {
        Process curr = Process.GetCurrentProcess();
        Process[] procs = Process.GetProcessesByName(curr.ProcessName);
        foreach (Process p in procs)
        {
            if ((p.Id != curr.Id) &&
                (p.MainModule.FileName == curr.MainModule.FileName))
                return p;
        }
        return null;
    }

并且以下内容:
[STAThread]
    static void Main()
    {
        if (PriorProcess() != null)
        {

            MessageBox.Show("Another instance of the app is already running.");
            return;
        }
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form());
    }

5
这种方式是不正确的。我可以通过两种方式运行您的程序——一种是作为二进制文件启动,另一种是从VS中启动。 - greenoldman
14
好的,@greenoldman,但是用户/客户从何时开始“调试”您的应用程序并选择在其旁边运行第二个(二进制)应用程序?与我在此网站上看到的基于互斥量的许多答案相比,这个答案非常好。 - jay_t55
7
我知道这已经过了好几年,但如果有人像我一样找到了这篇文章:如果用户不是管理员(至少在Win7中),Process.GetProcessesByName会抛出异常。 - George T
为确保您不会得到两个实例,请确保在返回之前放置Application.Exit()。 - S Nash

9

这是我在应用程序中使用的:

static void Main()
{
  bool mutexCreated = false;
  System.Threading.Mutex mutex = new System.Threading.Mutex( true, @"Local\slimCODE.slimKEYS.exe", out mutexCreated );

  if( !mutexCreated )
  {
    if( MessageBox.Show(
      "slimKEYS is already running. Hotkeys cannot be shared between different instances. Are you sure you wish to run this second instance?",
      "slimKEYS already running",
      MessageBoxButtons.YesNo,
      MessageBoxIcon.Question ) != DialogResult.Yes )
    {
      mutex.Close();
      return;
    }
  }

  // The usual stuff with Application.Run()

  mutex.Close();
}

2

单例应用程序的另一种方法是检查它们的哈希值。 在尝试使用互斥锁解决问题后(没有达到我的要求),我通过以下方式使其正常运行:

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool SetForegroundWindow(IntPtr hWnd);

    public Main()
    {
        InitializeComponent();

        Process current = Process.GetCurrentProcess();
        string currentmd5 = md5hash(current.MainModule.FileName);
        Process[] processlist = Process.GetProcesses();
        foreach (Process process in processlist)
        {
            if (process.Id != current.Id)
            {
                try
                {
                    if (currentmd5 == md5hash(process.MainModule.FileName))
                    {
                        SetForegroundWindow(process.MainWindowHandle);
                        Environment.Exit(0);
                    }
                }
                catch (/* your exception */) { /* your exception goes here */ }
            }
        }
    }

    private string md5hash(string file)
    {
        string check;
        using (FileStream FileCheck = File.OpenRead(file))
        {
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] md5Hash = md5.ComputeHash(FileCheck);
            check = BitConverter.ToString(md5Hash).Replace("-", "").ToLower();
        }

        return check;
    }

它仅通过进程ID检查MD5摘要。

如果找到此应用程序的实例,则会将焦点放在运行的应用程序上并退出自身。

您可以重命名它或对文件进行任何操作。如果MD5哈希值相同,则不会打开两次。

是否有人对此有建议?我知道已经有答案了,但也许有人正在寻找互斥替代方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接