创建单实例WPF应用程序的正确方法是什么?

在. NET(而不是windows窗体或控制台)下使用C#和WPF,创建只能作为单个实例运行的应用程序的正确方法是什么?

我知道这与一些被称为互斥锁的神秘事物有关,我很少能找到有人费心停下来解释其中一个是什么。

代码还需要通知已经运行的实例用户试图启动第二个实例,并且可能还传递任何命令行参数(如果存在)。

250283 次浏览

来自这里

跨进程Mutex的一个常见用途是确保一次只能运行程序的实例。这是它的完成方式:

class OneAtATimePlease {


// Use a name unique to the application (eg include your company URL)
static Mutex mutex = new Mutex (false, "oreilly.com OneAtATimeDemo");


static void Main()
{
// Wait 5 seconds if contended – in case another instance
// of the program is in the process of shutting down.
if (!mutex.WaitOne(TimeSpan.FromSeconds (5), false))
{
Console.WriteLine("Another instance of the app is running. Bye!");
return;
}


try
{
Console.WriteLine("Running - press Enter to exit");
Console.ReadLine();
}
finally
{
mutex.ReleaseMutex();
}
}
}

Mutex的一个好特性是,如果应用程序在没有首先调用ReleaseMutex的情况下终止,CLR将自动释放Mutex。

你可以使用Mutex类,但是你很快就会发现你需要实现代码来传递参数等等。嗯,当我阅读克里斯·塞尔的书时,我在WinForms编程时学到了一个技巧。这个技巧使用了框架中已经可用的逻辑。我不知道你,但是当我了解我可以在框架中重用的东西时,这通常是我采取的路线,而不是重新发明轮子。当然,除非它不能完成我想要的一切。

当我进入WPF时,我想出了一种在WPF应用程序中使用相同代码的方法。根据您的问题,此解决方案应该满足您的需求。

首先,我们需要创建我们的应用程序类。在这个类中,我们将覆盖OnStartup事件并创建一个名为Activate的方法,稍后将使用该方法。

public class SingleInstanceApplication : System.Windows.Application
{
protected override void OnStartup(System.Windows.StartupEventArgs e)
{
// Call the OnStartup event on our base class
base.OnStartup(e);


// Create our MainWindow and show it
MainWindow window = new MainWindow();
window.Show();
}


public void Activate()
{
// Reactivate the main window
MainWindow.Activate();
}
}

其次,我们需要创建一个可以管理我们的实例的类。在我们进行此操作之前,我们实际上要重用Microsoft. VisualBasic程序集中的一些代码。由于在此示例中我使用的是C#,因此我必须引用该程序集。如果你使用的是VB.NET,则无需做任何事情。我们将使用的类是WindowsFormsApplication ationBase并从中继承我们的实例管理器,然后利用属性和事件来处理单实例化。

public class SingleInstanceManager : Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
{
private SingleInstanceApplication _application;
private System.Collections.ObjectModel.ReadOnlyCollection<string> _commandLine;


public SingleInstanceManager()
{
IsSingleInstance = true;
}


protected override bool OnStartup(Microsoft.VisualBasic.ApplicationServices.StartupEventArgs eventArgs)
{
// First time _application is launched
_commandLine = eventArgs.CommandLine;
_application = new SingleInstanceApplication();
_application.Run();
return false;
}


protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
{
// Subsequent launches
base.OnStartupNextInstance(eventArgs);
_commandLine = eventArgs.CommandLine;
_application.Activate();
}
}

基本上,我们使用VB位来检测单个实例并进行相应的处理。OnStartup将在第一个实例加载时被触发。OnStartupNextInstance在应用程序再次重新运行时被触发。如您所见,我可以通过事件参数获取命令行上传递的内容。我将值设置为实例字段。您可以在此处解析命令行,也可以通过构造函数和对Activate方法的调用将其传递给您的应用程序。

第三,是时候创建我们的入口点了。我们将利用SingleInstance Manager,而不是像您通常所做的那样更新应用程序。

public class EntryPoint
{
[STAThread]
public static void Main(string[] args)
{
SingleInstanceManager manager = new SingleInstanceManager();
manager.Run(args);
}
}

好吧,我希望你能够遵循一切,并能够使用这个实现并使其成为你自己的。

您永远不应该使用命名互斥锁来实现单实例应用程序(或者至少不应该用于生产代码)。恶意代码可以很容易地DoS(拒绝服务)你的屁股…

这里有一个关于互斥解决方案的非常好的文章。文章描述的方法是有利的,原因有二。

首先,它不需要依赖于Microsoft. VisualBasic程序集。如果我的项目已经依赖于该程序集,我可能会提倡使用方法显示在另一个答案。但事实上,我不使用Microsoft. VisualBasic程序集,我宁愿不向我的项目添加不必要的依赖项。

其次,本文展示了当用户尝试启动另一个实例时如何将应用程序的现有实例带到前台。这是一个非常好的接触,这里描述的其他Mutex解决方案没有解决。


更新

截至8/1/2014,我上面链接的文章仍然活跃,但是博客有一段时间没有更新了。这让我担心它最终可能会消失,随之而来的是倡导的解决方案。我在这里复制文章的内容供后人使用。这些话完全属于博客所有者无理智编码

今天我想重构一些禁止我的应用程序的代码 运行多个自身实例。

以前我使用系统。诊断。过程搜索一个 进程列表中的myapp.exe实例。虽然这有效,但它 #36825;了很多,我想要更干净的东西。

知道我可以为此使用互斥锁(但从来没有这样做过 之前)我开始减少代码并简化我的生活。

在我的应用程序main的类中,我创建了一个名为互斥锁的静态:

static class Program
{
static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
[STAThread]
...
}

具有命名互斥锁允许我们跨堆栈同步 多线程和进程,这正是我正在寻找的魔力 为.

Mutex等一下有一个重载,为我们指定了一个时间量 等待。因为我们实际上并不想同步我们的代码 (更多只是检查它是否当前正在使用)我们使用重载 两个参数:Mutex. WaitOne(时间跨度超时)。 等待一个返回true如果它是能够进入,和false如果不是。 在这种情况下,我们根本不想等待;如果我们的互斥锁被 使用,跳过它,然后继续前进,因此我们传入TimeSpan. Zero(等待0 毫秒),并将exitContext设置为true,以便我们可以退出 在我们尝试获取锁之前同步上下文。使用 这,我们将我们的应用程序包装起来。运行代码如下所示:

static class Program
{
static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
[STAThread]
static void Main() {
if(mutex.WaitOne(TimeSpan.Zero, true)) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
mutex.ReleaseMutex();
} else {
MessageBox.Show("only one instance at a time");
}
}
}

因此,如果我们的应用程序正在运行,WaitOne将返回false,我们将得到一个 消息框。

而不是显示消息框,我选择使用一点Win32来 通知我正在运行的实例有人忘记它已经 运行(通过将自身带到所有其他窗口的顶部)。到 实现这一点,我使用发布消息向每个人广播自定义消息 窗口(自定义消息是用注册窗口消息注册的 我正在运行的应用程序,这意味着只有我的应用程序知道 它是)然后我的第二个实例退出。正在运行的应用程序实例 将收到该通知并处理它。为了做到这一点,我 在我的主窗体中覆盖WndProc并听取我的自定义 通知。当我收到通知时,我设置了表格的 将属性设置为true,使其位于顶部。

这是我最终得到的:

  • Program.cs
static class Program
{
static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
[STAThread]
static void Main() {
if(mutex.WaitOne(TimeSpan.Zero, true)) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
mutex.ReleaseMutex();
} else {
// send our Win32 message to make the currently running instance
// jump on top of all the other windows
NativeMethods.PostMessage(
(IntPtr)NativeMethods.HWND_BROADCAST,
NativeMethods.WM_SHOWME,
IntPtr.Zero,
IntPtr.Zero);
}
}
}
  • NativeMethods.cs
// this class just wraps some Win32 stuff that we're going to use
internal class NativeMethods
{
public const int HWND_BROADCAST = 0xffff;
public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
}
  • Form1.cs(正面部分)
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
if(m.Msg == NativeMethods.WM_SHOWME) {
ShowMe();
}
base.WndProc(ref m);
}
private void ShowMe()
{
if(WindowState == FormWindowState.Minimized) {
WindowState = FormWindowState.Normal;
}
// get our current "TopMost" value (ours will always be false though)
bool top = TopMost;
// make our form jump to the top of everything
TopMost = true;
// set it back to whatever it was
TopMost = top;
}
}

只是一些想法: 在某些情况下,要求只有一个应用程序实例并不像有些人希望你相信的那样“蹩脚”。如果允许单个用户访问应用程序的多个实例访问数据库(你知道,所有这些都更新了在用户机器上应用程序的多个实例中打开的所有记录,等等),数据库应用程序等会变得困难一个数量级。 首先,对于“名称冲突”,不要使用人类可读的名称——使用GUID代替,或者更好的是GUID+人类可读的名称。名称冲突的可能性刚刚从雷达上消失,Mutex并不关心。正如有人指出的那样,DOS攻击会很糟糕,但如果恶意人员不厌其烦地获取互斥锁名称并将其合并到他们的应用程序中,那么无论如何,你几乎都是一个目标,并且必须做更多的事情来保护自己,而不仅仅是摆弄一个互斥锁名称。 此外,如果使用变体: 新的Mutex(true,“一些GUID加上名称”,出AIsFirstInstance),您已经有了关于Mutex是否是第一个实例的指标。

好吧,我有一个一次性类,它可以很容易地用于大多数用例:

像这样使用它:

static void Main()
{
using (SingleInstanceMutex sim = new SingleInstanceMutex())
{
if (sim.IsOtherInstanceRunning)
{
Application.Exit();
}


// Initialize program here.
}
}

这里是:

/// <summary>
/// Represents a <see cref="SingleInstanceMutex"/> class.
/// </summary>
public partial class SingleInstanceMutex : IDisposable
{
#region Fields


/// <summary>
/// Indicator whether another instance of this application is running or not.
/// </summary>
private bool isNoOtherInstanceRunning;


/// <summary>
/// The <see cref="Mutex"/> used to ask for other instances of this application.
/// </summary>
private Mutex singleInstanceMutex = null;


/// <summary>
/// An indicator whether this object is beeing actively disposed or not.
/// </summary>
private bool disposed;


#endregion


#region Constructor


/// <summary>
/// Initializes a new instance of the <see cref="SingleInstanceMutex"/> class.
/// </summary>
public SingleInstanceMutex()
{
this.singleInstanceMutex = new Mutex(true, Assembly.GetCallingAssembly().FullName, out this.isNoOtherInstanceRunning);
}


#endregion


#region Properties


/// <summary>
/// Gets an indicator whether another instance of the application is running or not.
/// </summary>
public bool IsOtherInstanceRunning
{
get
{
return !this.isNoOtherInstanceRunning;
}
}


#endregion


#region Methods


/// <summary>
/// Closes the <see cref="SingleInstanceMutex"/>.
/// </summary>
public void Close()
{
this.ThrowIfDisposed();
this.singleInstanceMutex.Close();
}


public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}


private void Dispose(bool disposing)
{
if (!this.disposed)
{
/* Release unmanaged ressources */


if (disposing)
{
/* Release managed ressources */
this.Close();
}


this.disposed = true;
}
}


/// <summary>
/// Throws an exception if something is tried to be done with an already disposed object.
/// </summary>
/// <remarks>
/// All public methods of the class must first call this.
/// </remarks>
public void ThrowIfDisposed()
{
if (this.disposed)
{
throw new ObjectDisposedException(this.GetType().Name);
}
}


#endregion
}

这么一个看似简单的问题有这么多的答案。只是稍微改变一下,这是我对这个问题的解决方案。

创建Mutex可能会很麻烦,因为JIT-er只看到您在一小部分代码中使用它,并希望将其标记为已准备好使用垃圾回收机制。它非常想让您聪明地认为您不会使用那个Mutex那么长时间。实际上,您希望在应用程序运行期间一直保留这个Mutex。告诉垃圾收集器不要打扰您的Mutex的最佳方法是告诉它在不同代的车库收集中保持它的活动状态。示例:

var m = new Mutex(...);
...
GC.KeepAlive(m);

我从这个页面中提取了这个想法:http://www.ai.uga.edu/~mc/SingleInstance.html

一个新的使用Mutex和IPC的东西,并将任何命令行参数传递给正在运行的实例,是WPF单实例应用程序

这是我使用的。它结合了进程枚举来执行切换和互斥锁以防止“活动点击者”:

public partial class App
{
[DllImport("user32")]
private static extern int OpenIcon(IntPtr hWnd);


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


protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var p = Process
.GetProcessesByName(Process.GetCurrentProcess().ProcessName);
foreach (var t in p.Where(t => t.MainWindowHandle != IntPtr.Zero))
{
OpenIcon(t.MainWindowHandle);
SetForegroundWindow(t.MainWindowHandle);
Current.Shutdown();
return;
}


// there is a chance the user tries to click on the icon repeatedly
// and the process cannot be discovered yet
bool createdNew;
var mutex = new Mutex(true, "MyAwesomeApp",
out createdNew);  // must be a variable, though it is unused -
// we just need a bit of time until the process shows up
if (!createdNew)
{
Current.Shutdown();
return;
}


new Bootstrapper().Run();
}
}

MSDN实际上有一个C#和VB的示例应用程序来做到这一点:http://msdn.microsoft.com/en-us/library/ms771662(v=VS.90)

最常见和最可靠的技术 用于开发单实例 检测是使用Microsoft. NET 框架远程基础设施 (System. Remote ting)。Microsoft。NET 框架(2.0版)包括一个 窗口类型 它封装了所需的 远程处理功能。合并 将此类型转换为WPF应用程序,a 类型需要从它派生,并且是 用作应用程序之间的垫片 静态切入点方法、Main和 WPF应用程序的应用程序 类型。垫片检测到 应用程序首次启动,并且 当后续发射被 尝试,并且产量控制WPF 应用程序类型以确定如何 #36825;的发射。

  • 对于C#来说,人们只需深呼吸,忘记整个'我不想包含VisualBasic DLL'。因为这个斯科特·汉塞尔曼说以及这几乎是问题的最干净的解决方案,并且是由比您更了解框架的人设计的。
  • 从可用性的角度来看,如果您的用户正在加载一个应用程序并且它已经打开并且您给他们一个像'Another instance of the app is running. Bye'这样的错误消息,那么他们不会是一个非常满意的用户。您只需(在GUI应用程序中)切换到该应用程序并传入提供的参数-或者如果命令行参数没有意义,那么您必须弹出可能已最小化的应用程序。

该框架已经对此提供了支持-只是一些名为DLLMicrosoft.VisualBasic的白痴并没有将其放入Microsoft.ApplicationUtils或类似的东西中。克服它-或者打开Reflector。

提示:如果您完全按原样使用此方法,并且您已经有资源等App.xaml,则需要把这个也看看

我找到了更简单的解决方案,类似于Dale Ragan的,但略有修改。它几乎可以完成您需要的所有内容,并基于标准的Microsoft WindowsFormsApplication ationBase类。

首先,您创建SingleInstance Controller类,您可以在所有其他使用Windows窗体的单实例应用程序中使用它:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.VisualBasic.ApplicationServices;




namespace SingleInstanceController_NET
{
public class SingleInstanceController
: WindowsFormsApplicationBase
{
public delegate Form CreateMainForm();
public delegate void StartNextInstanceDelegate(Form mainWindow);
CreateMainForm formCreation;
StartNextInstanceDelegate onStartNextInstance;
public SingleInstanceController(CreateMainForm formCreation, StartNextInstanceDelegate onStartNextInstance)
{
// Set whether the application is single instance
this.formCreation = formCreation;
this.onStartNextInstance = onStartNextInstance;
this.IsSingleInstance = true;


this.StartupNextInstance += new StartupNextInstanceEventHandler(this_StartupNextInstance);
}


void this_StartupNextInstance(object sender, StartupNextInstanceEventArgs e)
{
if (onStartNextInstance != null)
{
onStartNextInstance(this.MainForm); // This code will be executed when the user tries to start the running program again,
// for example, by clicking on the exe file.
}                                       // This code can determine how to re-activate the existing main window of the running application.
}


protected override void OnCreateMainForm()
{
// Instantiate your main application form
this.MainForm = formCreation();
}


public void Run()
{
string[] commandLine = new string[0];
base.Run(commandLine);
}
}
}

然后,您可以在您的程序中使用它,如下所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using SingleInstanceController_NET;


namespace SingleInstance
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static Form CreateForm()
{
return new Form1(); // Form1 is used for the main window.
}


static void OnStartNextInstance(Form mainWindow) // When the user tries to restart the application again,
// the main window is activated again.
{
mainWindow.WindowState = FormWindowState.Maximized;
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
SingleInstanceController controller = new SingleInstanceController(CreateForm, OnStartNextInstance);
controller.Run();
}
}
}

程序和SingleInstanceController_NET解决方案都应引用Microsoft. VisualBasic。如果您只想在用户尝试重新启动正在运行的程序时以正常窗口的形式重新激活正在运行的应用程序,SingleInstance Controller中的第二个参数可以为空。在给定的示例中,窗口最大化。

这是一个允许您拥有应用程序单个实例的示例。当任何新实例加载时,它们会将其参数传递给正在运行的主实例。

public partial class App : Application
{
private static Mutex SingleMutex;
public static uint MessageId;


private void Application_Startup(object sender, StartupEventArgs e)
{
IntPtr Result;
IntPtr SendOk;
Win32.COPYDATASTRUCT CopyData;
string[] Args;
IntPtr CopyDataMem;
bool AllowMultipleInstances = false;


Args = Environment.GetCommandLineArgs();


// TODO: Replace {00000000-0000-0000-0000-000000000000} with your application's GUID
MessageId   = Win32.RegisterWindowMessage("{00000000-0000-0000-0000-000000000000}");
SingleMutex = new Mutex(false, "AppName");


if ((AllowMultipleInstances) || (!AllowMultipleInstances && SingleMutex.WaitOne(1, true)))
{
new Main();
}
else if (Args.Length > 1)
{
foreach (Process Proc in Process.GetProcesses())
{
SendOk = Win32.SendMessageTimeout(Proc.MainWindowHandle, MessageId, IntPtr.Zero, IntPtr.Zero,
Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
2000, out Result);


if (SendOk == IntPtr.Zero)
continue;
if ((uint)Result != MessageId)
continue;


CopyDataMem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32.COPYDATASTRUCT)));


CopyData.dwData = IntPtr.Zero;
CopyData.cbData = Args[1].Length*2;
CopyData.lpData = Marshal.StringToHGlobalUni(Args[1]);


Marshal.StructureToPtr(CopyData, CopyDataMem, false);


Win32.SendMessageTimeout(Proc.MainWindowHandle, Win32.WM_COPYDATA, IntPtr.Zero, CopyDataMem,
Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
5000, out Result);


Marshal.FreeHGlobal(CopyData.lpData);
Marshal.FreeHGlobal(CopyDataMem);
}


Shutdown(0);
}
}
}


public partial class Main : Window
{
private void Window_Loaded(object sender, RoutedEventArgs e)
{
HwndSource Source;


Source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
Source.AddHook(new HwndSourceHook(Window_Proc));
}


private IntPtr Window_Proc(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam, ref bool Handled)
{
Win32.COPYDATASTRUCT CopyData;
string Path;


if (Msg == Win32.WM_COPYDATA)
{
CopyData = (Win32.COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32.COPYDATASTRUCT));
Path = Marshal.PtrToStringUni(CopyData.lpData, CopyData.cbData / 2);


if (WindowState == WindowState.Minimized)
{
// Restore window from tray
}


// Do whatever we want with information


Activate();
Focus();
}


if (Msg == App.MessageId)
{
Handled = true;
return new IntPtr(App.MessageId);
}


return IntPtr.Zero;
}
}


public class Win32
{
public const uint WM_COPYDATA = 0x004A;


public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int    cbData;
public IntPtr lpData;
}


[Flags]
public enum SendMessageTimeoutFlags : uint
{
SMTO_NORMAL             = 0x0000,
SMTO_BLOCK              = 0x0001,
SMTO_ABORTIFHUNG        = 0x0002,
SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
}


[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
public static extern uint RegisterWindowMessage(string lpString);
[DllImport("user32.dll")]
public static extern IntPtr SendMessageTimeout(
IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam,
SendMessageTimeoutFlags fuFlags, uint uTimeout, out IntPtr lpdwResult);
}

看起来有一个非常好的方法来处理这个问题:

WPF单实例应用程序

这提供了一个类,您可以添加它来管理所有的互斥锁和消息传递,以简化您的实现,使其变得微不足道。

此代码应转到main方法。有关WPF中main方法的更多信息,请查看这里

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


private const int SW_SHOWMAXIMIZED = 3;


static void Main()
{
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);
return;
}
}

方法2

static void Main()
{
string procName = Process.GetCurrentProcess().ProcessName;
// get the list of all processes by that name


Process[] processes=Process.GetProcessesByName(procName);


if (processes.Length > 1)
{
MessageBox.Show(procName + " already running");
return;
}
else
{
// Application.Run(...);
}
}

备注:上述方法假设您的进程/应用程序有一个唯一的名称。因为它使用进程名称来查找是否有任何现有的处理器。所以,如果您的应用程序有一个非常常见的名称(即:记事本),上述方法将不起作用。

查看下面的代码。这是一个防止WPF应用程序多个实例的简单解决方案。

private void Application_Startup(object sender, StartupEventArgs e)
{
Process thisProc = Process.GetCurrentProcess();
if (Process.GetProcessesByName(thisProc.ProcessName).Length > 1)
{
MessageBox.Show("Application running");
Application.Current.Shutdown();
return;
}


var wLogin = new LoginWindow();


if (wLogin.ShowDialog() == true)
{
var wMain = new Main();
wMain.WindowState = WindowState.Maximized;
wMain.Show();
}
else
{
Application.Current.Shutdown();
}
}

通常,这是我用于单实例windows窗体应用程序的代码:

[STAThread]
public static void Main()
{
String assemblyName = Assembly.GetExecutingAssembly().GetName().Name;


using (Mutex mutex = new Mutex(false, assemblyName))
{
if (!mutex.WaitOne(0, false))
{
Boolean shownProcess = false;
Process currentProcess = Process.GetCurrentProcess();


foreach (Process process in Process.GetProcessesByName(currentProcess.ProcessName))
{
if (!process.Id.Equals(currentProcess.Id) && process.MainModule.FileName.Equals(currentProcess.MainModule.FileName) && !process.MainWindowHandle.Equals(IntPtr.Zero))
{
IntPtr windowHandle = process.MainWindowHandle;


if (NativeMethods.IsIconic(windowHandle))
NativeMethods.ShowWindow(windowHandle, ShowWindowCommand.Restore);


NativeMethods.SetForegroundWindow(windowHandle);


shownProcess = true;
}
}


if (!shownProcess)
MessageBox.Show(String.Format(CultureInfo.CurrentCulture, "An instance of {0} is already running!", assemblyName), assemblyName, MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1, (MessageBoxOptions)0);
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form());
}
}
}

其中原生组件是:

[DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean IsIconic([In] IntPtr windowHandle);


[DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean SetForegroundWindow([In] IntPtr windowHandle);


[DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean ShowWindow([In] IntPtr windowHandle, [In] ShowWindowCommand command);


public enum ShowWindowCommand : int
{
Hide                   = 0x0,
ShowNormal             = 0x1,
ShowMinimized          = 0x2,
ShowMaximized          = 0x3,
ShowNormalNotActive    = 0x4,
Minimize               = 0x6,
ShowMinimizedNotActive = 0x7,
ShowCurrentNotActive   = 0x8,
Restore                = 0x9,
ShowDefault            = 0xA,
ForceMinimize          = 0xB
}

以下代码是我的WCF命名管道解决方案,用于注册单实例应用程序。这很好,因为当另一个实例尝试启动时,它还会引发一个事件,并接收另一个实例的命令行。

它面向WPF,因为它使用System.Windows.StartupEventHandler类,但这可以很容易地修改。

此代码需要引用PresentationFrameworkSystem.ServiceModel

用法:

class Program
{
static void Main()
{
var applicationId = new Guid("b54f7b0d-87f9-4df9-9686-4d8fd76066dc");


if (SingleInstanceManager.VerifySingleInstance(applicationId))
{
SingleInstanceManager.OtherInstanceStarted += OnOtherInstanceStarted;


// Start the application
}
}


static void OnOtherInstanceStarted(object sender, StartupEventArgs e)
{
// Do something in response to another instance starting up.
}
}

源代码:

/// <summary>
/// A class to use for single-instance applications.
/// </summary>
public static class SingleInstanceManager
{
/// <summary>
/// Raised when another instance attempts to start up.
/// </summary>
public static event StartupEventHandler OtherInstanceStarted;


/// <summary>
/// Checks to see if this instance is the first instance running on this machine.  If it is not, this method will
/// send the main instance this instance's startup information.
/// </summary>
/// <param name="guid">The application's unique identifier.</param>
/// <returns>True if this instance is the main instance.</returns>
public static bool VerifySingleInstace(Guid guid)
{
if (!AttemptPublishService(guid))
{
NotifyMainInstance(guid);


return false;
}


return true;
}


/// <summary>
/// Attempts to publish the service.
/// </summary>
/// <param name="guid">The application's unique identifier.</param>
/// <returns>True if the service was published successfully.</returns>
private static bool AttemptPublishService(Guid guid)
{
try
{
ServiceHost serviceHost = new ServiceHost(typeof(SingleInstance));
NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
serviceHost.AddServiceEndpoint(typeof(ISingleInstance), binding, CreateAddress(guid));
serviceHost.Open();


return true;
}
catch
{
return false;
}
}


/// <summary>
/// Notifies the main instance that this instance is attempting to start up.
/// </summary>
/// <param name="guid">The application's unique identifier.</param>
private static void NotifyMainInstance(Guid guid)
{
NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
EndpointAddress remoteAddress = new EndpointAddress(CreateAddress(guid));
using (ChannelFactory<ISingleInstance> factory = new ChannelFactory<ISingleInstance>(binding, remoteAddress))
{
ISingleInstance singleInstance = factory.CreateChannel();
singleInstance.NotifyMainInstance(Environment.GetCommandLineArgs());
}
}


/// <summary>
/// Creates an address to publish/contact the service at based on a globally unique identifier.
/// </summary>
/// <param name="guid">The identifier for the application.</param>
/// <returns>The address to publish/contact the service.</returns>
private static string CreateAddress(Guid guid)
{
return string.Format(CultureInfo.CurrentCulture, "net.pipe://localhost/{0}", guid);
}


/// <summary>
/// The interface that describes the single instance service.
/// </summary>
[ServiceContract]
private interface ISingleInstance
{
/// <summary>
/// Notifies the main instance that another instance of the application attempted to start.
/// </summary>
/// <param name="args">The other instance's command-line arguments.</param>
[OperationContract]
void NotifyMainInstance(string[] args);
}


/// <summary>
/// The implementation of the single instance service interface.
/// </summary>
private class SingleInstance : ISingleInstance
{
/// <summary>
/// Notifies the main instance that another instance of the application attempted to start.
/// </summary>
/// <param name="args">The other instance's command-line arguments.</param>
public void NotifyMainInstance(string[] args)
{
if (OtherInstanceStarted != null)
{
Type type = typeof(StartupEventArgs);
ConstructorInfo constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
StartupEventArgs e = (StartupEventArgs)constructor.Invoke(null);
FieldInfo argsField = type.GetField("_args", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(argsField != null);
argsField.SetValue(e, args);


OtherInstanceStarted(null, e);
}
}
}
}

作为标记答案参考的代码C#. NET单实例应用程序是一个很好的开始。

然而,我发现当已经存在的实例打开了一个模态对话框时,它不能很好地处理这种情况,无论该对话框是托管对话框(像另一个Form,比如about框),还是非托管对话框(像OpenFileDialog,即使使用标准. NET类)。使用原始代码,主表单被激活,但模态对话框保持非活动状态,这看起来很奇怪,而且用户必须单击它才能继续使用该应用程序。

因此,我创建了一个SingleInstance实用程序类来自动处理Winform和WPF应用程序的所有这些。

Winform

1)像这样修改Program类:

static class Program
{
public static readonly SingleInstance Singleton = new SingleInstance(typeof(Program).FullName);


[STAThread]
static void Main(string[] args)
{
// NOTE: if this always return false, close & restart Visual Studio
// this is probably due to the vshost.exe thing
Singleton.RunFirstInstance(() =>
{
SingleInstanceMain(args);
});
}


public static void SingleInstanceMain(string[] args)
{
// standard code that was in Main now goes here
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}

2)像这样修改主窗口类:

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}


protected override void WndProc(ref Message m)
{
// if needed, the singleton will restore this window
Program.Singleton.OnWndProc(this, m, true);


// TODO: handle specific messages here if needed
base.WndProc(ref m);
}
}

WPF:

1)像这样修改App页面(并确保将其构建操作设置为页面,以便能够重新定义Main方法):

public partial class App : Application
{
public static readonly SingleInstance Singleton = new SingleInstance(typeof(App).FullName);


[STAThread]
public static void Main(string[] args)
{
// NOTE: if this always return false, close & restart Visual Studio
// this is probably due to the vshost.exe thing
Singleton.RunFirstInstance(() =>
{
SingleInstanceMain(args);
});
}


public static void SingleInstanceMain(string[] args)
{
// standard code that was in Main now goes here
App app = new App();
app.InitializeComponent();
app.Run();
}
}

2)像这样修改主窗口类:

public partial class MainWindow : Window
{
private HwndSource _source;


public MainWindow()
{
InitializeComponent();
}


protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
_source = (HwndSource)PresentationSource.FromVisual(this);
_source.AddHook(HwndSourceHook);
}


protected virtual IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// if needed, the singleton will restore this window
App.Singleton.OnWndProc(hwnd, msg, wParam, lParam, true, true);


// TODO: handle other specific message
return IntPtr.Zero;
}

这是实用程序类:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;


namespace SingleInstanceUtilities
{
public sealed class SingleInstance
{
private const int HWND_BROADCAST = 0xFFFF;


[DllImport("user32.dll")]
private static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);


[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int RegisterWindowMessage(string message);


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


public SingleInstance(string uniqueName)
{
if (uniqueName == null)
throw new ArgumentNullException("uniqueName");


Mutex = new Mutex(true, uniqueName);
Message = RegisterWindowMessage("WM_" + uniqueName);
}


public Mutex Mutex { get; private set; }
public int Message { get; private set; }


public void RunFirstInstance(Action action)
{
RunFirstInstance(action, IntPtr.Zero, IntPtr.Zero);
}


// NOTE: if this always return false, close & restart Visual Studio
// this is probably due to the vshost.exe thing
public void RunFirstInstance(Action action, IntPtr wParam, IntPtr lParam)
{
if (action == null)
throw new ArgumentNullException("action");


if (WaitForMutext(wParam, lParam))
{
try
{
action();
}
finally
{
ReleaseMutex();
}
}
}


public static void ActivateWindow(IntPtr hwnd)
{
if (hwnd == IntPtr.Zero)
return;


FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(hwnd));
}


public void OnWndProc(IntPtr hwnd, int m, IntPtr wParam, IntPtr lParam, bool restorePlacement, bool activate)
{
if (m == Message)
{
if (restorePlacement)
{
WindowPlacement placement = WindowPlacement.GetPlacement(hwnd, false);
if (placement.IsValid && placement.IsMinimized)
{
const int SW_SHOWNORMAL = 1;
placement.ShowCmd = SW_SHOWNORMAL;
placement.SetPlacement(hwnd);
}
}


if (activate)
{
SetForegroundWindow(hwnd);
FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(hwnd));
}
}
}


#if WINFORMS // define this for Winforms apps
public void OnWndProc(System.Windows.Forms.Form form, int m, IntPtr wParam, IntPtr lParam, bool activate)
{
if (form == null)
throw new ArgumentNullException("form");


if (m == Message)
{
if (activate)
{
if (form.WindowState == System.Windows.Forms.FormWindowState.Minimized)
{
form.WindowState = System.Windows.Forms.FormWindowState.Normal;
}


form.Activate();
FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(form.Handle));
}
}
}


public void OnWndProc(System.Windows.Forms.Form form, System.Windows.Forms.Message m, bool activate)
{
if (form == null)
throw new ArgumentNullException("form");


OnWndProc(form, m.Msg, m.WParam, m.LParam, activate);
}
#endif


public void ReleaseMutex()
{
Mutex.ReleaseMutex();
}


public bool WaitForMutext(bool force, IntPtr wParam, IntPtr lParam)
{
bool b = PrivateWaitForMutext(force);
if (!b)
{
PostMessage((IntPtr)HWND_BROADCAST, Message, wParam, lParam);
}
return b;
}


public bool WaitForMutext(IntPtr wParam, IntPtr lParam)
{
return WaitForMutext(false, wParam, lParam);
}


private bool PrivateWaitForMutext(bool force)
{
if (force)
return true;


try
{
return Mutex.WaitOne(TimeSpan.Zero, true);
}
catch (AbandonedMutexException)
{
return true;
}
}
}


// NOTE: don't add any field or public get/set property, as this must exactly map to Windows' WINDOWPLACEMENT structure
[StructLayout(LayoutKind.Sequential)]
public struct WindowPlacement
{
public int Length { get; set; }
public int Flags { get; set; }
public int ShowCmd { get; set; }
public int MinPositionX { get; set; }
public int MinPositionY { get; set; }
public int MaxPositionX { get; set; }
public int MaxPositionY { get; set; }
public int NormalPositionLeft { get; set; }
public int NormalPositionTop { get; set; }
public int NormalPositionRight { get; set; }
public int NormalPositionBottom { get; set; }


[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl);


[DllImport("user32.dll", SetLastError = true)]
private static extern bool GetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl);


private const int SW_SHOWMINIMIZED = 2;


public bool IsMinimized
{
get
{
return ShowCmd == SW_SHOWMINIMIZED;
}
}


public bool IsValid
{
get
{
return Length == Marshal.SizeOf(typeof(WindowPlacement));
}
}


public void SetPlacement(IntPtr windowHandle)
{
SetWindowPlacement(windowHandle, ref this);
}


public static WindowPlacement GetPlacement(IntPtr windowHandle, bool throwOnError)
{
WindowPlacement placement = new WindowPlacement();
if (windowHandle == IntPtr.Zero)
return placement;


placement.Length = Marshal.SizeOf(typeof(WindowPlacement));
if (!GetWindowPlacement(windowHandle, ref placement))
{
if (throwOnError)
throw new Win32Exception(Marshal.GetLastWin32Error());


return new WindowPlacement();
}
return placement;
}
}


public static class FormUtilities
{
[DllImport("user32.dll")]
private static extern IntPtr GetWindow(IntPtr hWnd, int uCmd);


[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetActiveWindow(IntPtr hWnd);


[DllImport("user32.dll")]
private static extern bool IsWindowVisible(IntPtr hWnd);


[DllImport("kernel32.dll")]
public static extern int GetCurrentThreadId();


private delegate bool EnumChildrenCallback(IntPtr hwnd, IntPtr lParam);


[DllImport("user32.dll")]
private static extern bool EnumThreadWindows(int dwThreadId, EnumChildrenCallback lpEnumFunc, IntPtr lParam);


private class ModalWindowUtil
{
private const int GW_OWNER = 4;
private int _maxOwnershipLevel;
private IntPtr _maxOwnershipHandle;


private bool EnumChildren(IntPtr hwnd, IntPtr lParam)
{
int level = 1;
if (IsWindowVisible(hwnd) && IsOwned(lParam, hwnd, ref level))
{
if (level > _maxOwnershipLevel)
{
_maxOwnershipHandle = hwnd;
_maxOwnershipLevel = level;
}
}
return true;
}


private static bool IsOwned(IntPtr owner, IntPtr hwnd, ref int level)
{
IntPtr o = GetWindow(hwnd, GW_OWNER);
if (o == IntPtr.Zero)
return false;


if (o == owner)
return true;


level++;
return IsOwned(owner, o, ref level);
}


public static void ActivateWindow(IntPtr hwnd)
{
if (hwnd != IntPtr.Zero)
{
SetActiveWindow(hwnd);
}
}


public static IntPtr GetModalWindow(IntPtr owner)
{
ModalWindowUtil util = new ModalWindowUtil();
EnumThreadWindows(GetCurrentThreadId(), util.EnumChildren, owner);
return util._maxOwnershipHandle; // may be IntPtr.Zero
}
}


public static void ActivateWindow(IntPtr hwnd)
{
ModalWindowUtil.ActivateWindow(hwnd);
}


public static IntPtr GetModalWindow(IntPtr owner)
{
return ModalWindowUtil.GetModalWindow(owner);
}
}
}

使用互斥锁解决方案:

using System;
using System.Windows.Forms;
using System.Threading;


namespace OneAndOnlyOne
{
static class Program
{
static String _mutexID = " // generate guid"
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);


Boolean _isNotRunning;
using (Mutex _mutex = new Mutex(true, _mutexID, out _isNotRunning))
{
if (_isNotRunning)
{
Application.Run(new Form1());
}
else
{
MessageBox.Show("An instance is already running.");
return;
}
}
}
}
}

这是我使用的一个轻量级解决方案,它允许应用程序将已经存在的窗口带到前台,而无需诉诸自定义窗口消息或盲目搜索进程名称。

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


static readonly string guid = "<Application Guid>";


static void Main()
{
Mutex mutex = null;
if (!CreateMutex(out mutex))
return;


// Application startup code.


Environment.SetEnvironmentVariable(guid, null, EnvironmentVariableTarget.User);
}


static bool CreateMutex(out Mutex mutex)
{
bool createdNew = false;
mutex = new Mutex(false, guid, out createdNew);


if (createdNew)
{
Process process = Process.GetCurrentProcess();
string value = process.Id.ToString();


Environment.SetEnvironmentVariable(guid, value, EnvironmentVariableTarget.User);
}
else
{
string value = Environment.GetEnvironmentVariable(guid, EnvironmentVariableTarget.User);
Process process = null;
int processId = -1;


if (int.TryParse(value, out processId))
process = Process.GetProcessById(processId);


if (process == null || !SetForegroundWindow(process.MainWindowHandle))
MessageBox.Show("Unable to start application. An instance of this application is already running.");
}


return createdNew;
}

编辑:你也可以静态地存储和初始化互斥锁和createdNew,但是你需要在完成后显式地处置/释放互斥锁。就个人而言,我更喜欢保持互斥锁在本地,因为即使应用程序关闭而没有到达Main的末尾,它也会被自动处理掉。

更新2017-01-25。在尝试了几件事之后,我决定用VisualBasic.dll它更容易,效果更好(至少对我来说)。我让我之前的答案作为参考…

作为参考,这就是我在没有传递参数的情况下所做的(我找不到任何理由这样做……我的意思是一个带有参数的应用程序,可以从一个实例传递到另一个实例)。 如果需要文件关联,那么应该为每个文档实例化一个应用程序(根据用户的标准期望)。如果你必须将args传递给现有的应用程序,我想我会使用vb dll。

不传递args(只是单实例应用程序),我更喜欢不注册新的Windows消息,也不覆盖Matt Davis解决方案中定义的消息循环。虽然添加VisualBasic dll没什么大不了的,但我更喜欢不只是为了做单实例应用程序而添加一个新引用。此外,我更喜欢使用Main实例化一个新类,而不是从App. Startup overriver调用关闭以确保尽快退出。

希望任何人都会喜欢它……或者会激发一点灵感:-)

项目启动类应设置为“SingleInstance App”。

public class SingleInstanceApp
{
[STAThread]
public static void Main(string[] args)
{
Mutex _mutexSingleInstance = new Mutex(true, "MonitorMeSingleInstance");


if (_mutexSingleInstance.WaitOne(TimeSpan.Zero, true))
{
try
{
var app = new App();
app.InitializeComponent();
app.Run();


}
finally
{
_mutexSingleInstance.ReleaseMutex();
_mutexSingleInstance.Close();
}
}
else
{
MessageBox.Show("One instance is already running.");


var processes = Process.GetProcessesByName(Assembly.GetEntryAssembly().GetName().Name);
{
if (processes.Length > 1)
{
foreach (var process in processes)
{
if (process.Id != Process.GetCurrentProcess().Id)
{
WindowHelper.SetForegroundWindow(process.MainWindowHandle);
}
}
}
}
}
}
}

窗口助手:

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;


namespace HQ.Util.Unmanaged
{
public class WindowHelper
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);

您还可以使用CodeFluent运行时这是一组免费的工具。它提供了一个单实例类来实现单个实例应用程序。

我添加了一个sendMessage方法到Native方法类。

显然,如果应用程序没有显示在任务栏中,postMessage方法是有效的,但是使用sendMessage方法解决了这个问题。

class NativeMethods
{
public const int HWND_BROADCAST = 0xffff;
public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
}

这里有一个解决方案:

Protected Overrides Sub OnStartup(e As StartupEventArgs)
Const appName As String = "TestApp"
Dim createdNew As Boolean
_mutex = New Mutex(True, appName, createdNew)
If Not createdNew Then
'app is already running! Exiting the application
MessageBox.Show("Application is already running.")
Application.Current.Shutdown()
End If
MyBase.OnStartup(e)
End Sub

这是通过事件实现的同样的事情。

public enum ApplicationSingleInstanceMode
{
CurrentUserSession,
AllSessionsOfCurrentUser,
Pc
}


public class ApplicationSingleInstancePerUser: IDisposable
{
private readonly EventWaitHandle _event;


/// <summary>
/// Shows if the current instance of ghost is the first
/// </summary>
public bool FirstInstance { get; private set; }


/// <summary>
/// Initializes
/// </summary>
/// <param name="applicationName">The application name</param>
/// <param name="mode">The single mode</param>
public ApplicationSingleInstancePerUser(string applicationName, ApplicationSingleInstanceMode mode = ApplicationSingleInstanceMode.CurrentUserSession)
{
string name;
if (mode == ApplicationSingleInstanceMode.CurrentUserSession)
name = $"Local\\{applicationName}";
else if (mode == ApplicationSingleInstanceMode.AllSessionsOfCurrentUser)
name = $"Global\\{applicationName}{Environment.UserDomainName}";
else
name = $"Global\\{applicationName}";


try
{
bool created;
_event = new EventWaitHandle(false, EventResetMode.ManualReset, name, out created);
FirstInstance = created;
}
catch
{
}
}


public void Dispose()
{
_event.Dispose();
}
}

这就是我最终处理这个问题的方式。请注意,调试代码仍在那里进行测试。此代码位于App.xaml.cs文件中的OnStartup中。(WPF)

        // Process already running ?
if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1)
{


// Show your error message
MessageBox.Show("xxx is already running.  \r\n\r\nIf the original process is hung up you may need to restart your computer, or kill the current xxx process using the task manager.", "xxx is already running!", MessageBoxButton.OK, MessageBoxImage.Exclamation);


// This process
Process currentProcess = Process.GetCurrentProcess();


// Get all processes running on the local computer.
Process[] localAll = Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName);


// ID of this process...
int temp = currentProcess.Id;
MessageBox.Show("This Process ID:  " + temp.ToString());


for (int i = 0; i < localAll.Length; i++)
{
// Find the other process
if (localAll[i].Id != currentProcess.Id)
{
MessageBox.Show("Original Process ID (Switching to):  " + localAll[i].Id.ToString());


// Switch to it...
SetForegroundWindow(localAll[i].MainWindowHandle);


}
}


Application.Current.Shutdown();


}

这可能有我还没有发现的问题。如果我遇到任何问题,我会更新我的答案。

这是我的2美分

 static class Program
{
[STAThread]
static void Main()
{
bool createdNew;
using (new Mutex(true, "MyApp", out createdNew))
{
if (createdNew) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var mainClass = new SynGesturesLogic();
Application.ApplicationExit += mainClass.tray_exit;
Application.Run();
}
else
{
var current = Process.GetCurrentProcess();
foreach (var process in Process.GetProcessesByName(current.ProcessName).Where(process => process.Id != current.Id))
{
NativeMethods.SetForegroundWindow(process.MainWindowHandle);
break;
}
}
}
}
}

如果从其他路径调用exe,我喜欢允许多个实例的解决方案。我修改了CharithJ解决方案方法1:

   static class Program {
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
[DllImport("User32.dll")]
public static extern Int32 SetForegroundWindow(IntPtr hWnd);
[STAThread]
static void Main() {
Process currentProcess = Process.GetCurrentProcess();
foreach (var process in Process.GetProcesses()) {
try {
if ((process.Id != currentProcess.Id) &&
(process.ProcessName == currentProcess.ProcessName) &&
(process.MainModule.FileName == currentProcess.MainModule.FileName)) {
ShowWindow(process.MainWindowHandle, 5); // const int SW_SHOW = 5; //Activates the window and displays it in its current size and position.
SetForegroundWindow(process.MainWindowHandle);
return;
}
} catch (Exception ex) {
//ignore Exception "Access denied "
}
}


Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}

不使用Mutex,简单的回答:

System.Diagnostics;
...
string thisprocessname = Process.GetCurrentProcess().ProcessName;


if (Process.GetProcesses().Count(p => p.ProcessName == thisprocessname) > 1)
return;

将其放入Program.Main()中。
示例

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;


namespace Sample
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
//simple add Diagnostics namespace, and these 3 lines below
string thisprocessname = Process.GetCurrentProcess().ProcessName;
if (Process.GetProcesses().Count(p => p.ProcessName == thisprocessname) > 1)
return;


Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Sample());
}
}
}

您可以将MessageBox.Show添加到if语句并放置“应用程序已运行”。
这可能对你有帮助

简单地使用StreamWriter,这个怎么样?

System.IO.File.StreamWriter OpenFlag = null;   //globally

try
{
OpenFlag = new StreamWriter(Path.GetTempPath() + "OpenedIfRunning");
}
catch (System.IO.IOException) //file in use
{
Environment.Exit(0);
}

基于命名互斥锁的方法不是跨平台的,因为命名互斥锁在Mono中不是全局的。基于进程枚举的方法没有任何同步,并可能导致不正确的行为(例如,同时启动的多个进程可能都根据时间自行终止)。基于窗口系统的方法在控制台应用程序中是不可取的。这个解决方案建立在Divin的答案之上,解决了所有这些问题:

using System;
using System.IO;


namespace TestCs
{
public class Program
{
// The app id must be unique. Generate a new guid for your application.
public static string AppId = "01234567-89ab-cdef-0123-456789abcdef";


// The stream is stored globally to ensure that it won't be disposed before the application terminates.
public static FileStream UniqueInstanceStream;


public static int Main(string[] args)
{
EnsureUniqueInstance();


// Your code here.


return 0;
}


private static void EnsureUniqueInstance()
{
// Note: If you want the check to be per-user, use Environment.SpecialFolder.ApplicationData instead.
string lockDir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"UniqueInstanceApps");
string lockPath = Path.Combine(lockDir, $"{AppId}.unique");


Directory.CreateDirectory(lockDir);


try
{
// Create the file with exclusive write access. If this fails, then another process is executing.
UniqueInstanceStream = File.Open(lockPath, FileMode.Create, FileAccess.Write, FileShare.None);


// Although only the line above should be sufficient, when debugging with a vshost on Visual Studio
// (that acts as a proxy), the IO exception isn't passed to the application before a Write is executed.
UniqueInstanceStream.Write(new byte[] { 0 }, 0, 1);
UniqueInstanceStream.Flush();
}
catch
{
throw new Exception("Another instance of the application is already running.");
}
}
}
}

[我在下面提供了控制台和wpf应用程序的示例代码。]

您只需在创建命名的Mutex实例后检查createdNew变量的值(下面的示例!)。

布尔值createdNew将返回false:

如果名为“YourApplication ationNamehere”的Mutex实例已经 在系统某处创建

布尔值createdNew将返回true:

如果这是第一个名为“YourApplication ationNamehere”的互斥锁 系统。


控制台应用程序-示例:

static Mutex m = null;


static void Main(string[] args)
{
const string mutexName = "YourApplicationNameHere";
bool createdNew = false;


try
{
// Initializes a new instance of the Mutex class with a Boolean value that indicates
// whether the calling thread should have initial ownership of the mutex, a string that is the name of the mutex,
// and a Boolean value that, when the method returns, indicates whether the calling thread was granted initial ownership of the mutex.


using (m = new Mutex(true, mutexName, out createdNew))
{
if (!createdNew)
{
Console.WriteLine("instance is alreday running... shutting down !!!");
Console.Read();
return; // Exit the application
}


// Run your windows forms app here
Console.WriteLine("Single instance app is running!");
Console.ReadLine();
}




}
catch (Exception ex)
{


Console.WriteLine(ex.Message);
Console.ReadLine();
}
}

WPF示例:

public partial class App : Application
{
static Mutex m = null;


protected override void OnStartup(StartupEventArgs e)
{


const string mutexName = "YourApplicationNameHere";
bool createdNew = false;


try
{
// Initializes a new instance of the Mutex class with a Boolean value that indicates
// whether the calling thread should have initial ownership of the mutex, a string that is the name of the mutex,
// and a Boolean value that, when the method returns, indicates whether the calling thread was granted initial ownership of the mutex.


m = new Mutex(true, mutexName, out createdNew);


if (!createdNew)
{
Current.Shutdown(); // Exit the application
}


}
catch (Exception)
{
throw;
}


base.OnStartup(e);
}




protected override void OnExit(ExitEventArgs e)
{
if (m != null)
{
m.Dispose();
}
base.OnExit(e);
}
}

一个节省时间的C#Winform解决方案…

Program.cs:

using System;
using System.Windows.Forms;
// needs reference to Microsoft.VisualBasic
using Microsoft.VisualBasic.ApplicationServices;


namespace YourNamespace
{
public class SingleInstanceController : WindowsFormsApplicationBase
{
public SingleInstanceController()
{
this.IsSingleInstance = true;
}


protected override void OnStartupNextInstance(StartupNextInstanceEventArgs e)
{
e.BringToForeground = true;
base.OnStartupNextInstance(e);
}


protected override void OnCreateMainForm()
{
this.MainForm = new Form1();
}
}


static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
string[] args = Environment.GetCommandLineArgs();
SingleInstanceController controller = new SingleInstanceController();
controller.Run(args);
}
}
}

我在这里找不到短解,所以我希望有人会喜欢这个:

更新2018-09-20

将此代码放在您的Program.cs中:

using System.Diagnostics;


static void Main()
{
Process thisProcess = Process.GetCurrentProcess();
Process[] allProcesses = Process.GetProcessesByName(thisProcess.ProcessName);
if (allProcesses.Length > 1)
{
// Don't put a MessageBox in here because the user could spam this MessageBox.
return;
}


// Optional code. If you don't want that someone runs your ".exe" with a different name:


string exeName = AppDomain.CurrentDomain.FriendlyName;
// in debug mode, don't forget that you don't use your normal .exe name.
// Debug uses the .vshost.exe.
if (exeName != "the name of your executable.exe")
{
// You can add a MessageBox here if you want.
// To point out to users that the name got changed and maybe what the name should be or something like that^^
MessageBox.Show("The executable name should be \"the name of your executable.exe\"",
"Wrong executable name", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}


// Following code is default code:
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}

我在解决方案中使用互斥锁来防止多个实例。

static Mutex mutex = null;
//A string that is the name of the mutex
string mutexName = @"Global\test";
//Prevent Multiple Instances of Application
bool onlyInstance = false;
mutex = new Mutex(true, mutexName, out onlyInstance);


if (!onlyInstance)
{
MessageBox.Show("You are already running this application in your system.", "Already Running..", MessageBoxButton.OK);
Application.Current.Shutdown();
}

请检查这里提出的解决方案,该解决方案使用信号量来确定现有实例是否已经运行,适用于WPF应用程序,并且可以使用TcpListener和TcpClient将参数从第二个实例传递给第一个已经运行的实例:

它也适用于. NET Core,而不仅仅适用于. NET Framework。

我最喜欢的解决方案来自MVP Daniel Vaughan: 强制执行单实例Wpf应用程序

它使用MemoryMappdFile将命令行参数发送到第一个实例:

/// <summary>
/// This class allows restricting the number of executables in execution, to one.
/// </summary>
public sealed class SingletonApplicationEnforcer
{
readonly Action<IEnumerable<string>> processArgsFunc;
readonly string applicationId;
Thread thread;
string argDelimiter = "_;;_";


/// <summary>
/// Gets or sets the string that is used to join
/// the string array of arguments in memory.
/// </summary>
/// <value>The arg delimeter.</value>
public string ArgDelimeter
{
get
{
return argDelimiter;
}
set
{
argDelimiter = value;
}
}


/// <summary>
/// Initializes a new instance of the <see cref="SingletonApplicationEnforcer"/> class.
/// </summary>
/// <param name="processArgsFunc">A handler for processing command line args
/// when they are received from another application instance.</param>
/// <param name="applicationId">The application id used
/// for naming the <seealso cref="EventWaitHandle"/>.</param>
public SingletonApplicationEnforcer(Action<IEnumerable<string>> processArgsFunc,
string applicationId = "DisciplesRock")
{
if (processArgsFunc == null)
{
throw new ArgumentNullException("processArgsFunc");
}
this.processArgsFunc = processArgsFunc;
this.applicationId = applicationId;
}


/// <summary>
/// Determines if this application instance is not the singleton instance.
/// If this application is not the singleton, then it should exit.
/// </summary>
/// <returns><c>true</c> if the application should shutdown,
/// otherwise <c>false</c>.</returns>
public bool ShouldApplicationExit()
{
bool createdNew;
string argsWaitHandleName = "ArgsWaitHandle_" + applicationId;
string memoryFileName = "ArgFile_" + applicationId;


EventWaitHandle argsWaitHandle = new EventWaitHandle(
false, EventResetMode.AutoReset, argsWaitHandleName, out createdNew);


GC.KeepAlive(argsWaitHandle);


if (createdNew)
{
/* This is the main, or singleton application.
* A thread is created to service the MemoryMappedFile.
* We repeatedly examine this file each time the argsWaitHandle
* is Set by a non-singleton application instance. */
thread = new Thread(() =>
{
try
{
using (MemoryMappedFile file = MemoryMappedFile.CreateOrOpen(memoryFileName, 10000))
{
while (true)
{
argsWaitHandle.WaitOne();
using (MemoryMappedViewStream stream = file.CreateViewStream())
{
var reader = new BinaryReader(stream);
string args;
try
{
args = reader.ReadString();
}
catch (Exception ex)
{
Debug.WriteLine("Unable to retrieve string. " + ex);
continue;
}
string[] argsSplit = args.Split(new string[] { argDelimiter },
StringSplitOptions.RemoveEmptyEntries);
processArgsFunc(argsSplit);
}


}
}
}
catch (Exception ex)
{
Debug.WriteLine("Unable to monitor memory file. " + ex);
}
});


thread.IsBackground = true;
thread.Start();
}
else
{
/* Non singleton application instance.
* Should exit, after passing command line args to singleton process,
* via the MemoryMappedFile. */
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting(memoryFileName))
{
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
var writer = new BinaryWriter(stream);
string[] args = Environment.GetCommandLineArgs();
string joined = string.Join(argDelimiter, args);
writer.Write(joined);
}
}
argsWaitHandle.Set();
}


return !createdNew;
}
}

基于马特·戴维斯的回答,为了方便起见,包装成一个类。

public static class SingleAppInstanceChecker
{
/// <summary>
/// Arbitrary unique string
/// </summary>
private static Mutex _mutex = new Mutex(true, "0d12ad74-026f-40c3-bdae-e178ddee8602");


public static bool IsNotRunning()
{
return _mutex.WaitOne(TimeSpan.Zero, true);
}
}

示例用法:

private void Application_Startup(object sender, StartupEventArgs e)
{
if (!SingleAppInstanceChecker.IsNotRunning())
{
MessageBox.Show("Application is already running.");


// Exit application using:
// Environment.Exit(1);
// Application.Current.Shutdown();
// Etc...


return;
}
    

// Allow startup and continue with normal processing
// ...
}

这是我的整个App.xaml.cs,这段代码还将启动的程序实例带到前台:

public partial class App : Application
{
private static Mutex _mutex = null;


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


[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);


protected override void OnStartup(StartupEventArgs e)
{
_mutex = new Mutex(true, "YourMutexName", out var createdNew);


if (!createdNew)
{
using (var currentProcess = Process.GetCurrentProcess())
{
foreach (var process in Process.GetProcessesByName(currentProcess.ProcessName))
{
if (process.Id != currentProcess.Id)
{
ShowWindow(process.MainWindowHandle, 9);
SetForegroundWindow(process.MainWindowHandle);
}
process.Dispose();
}
}
// app is already running! Exiting the application
Shutdown();
}


base.OnStartup(e);
}


protected override void OnExit(ExitEventArgs e)
{
_mutex.Dispose();
base.OnExit(e);
}
}