在.NET(而不是Windows Forms或控制台)下使用C#和WPF ,创建只能作为单个实例运行的应用程序的正确方法是什么?
我知道它与某种称为互斥体的神话事物有关,我很少能找到一个烦人的人来阻止并解释其中的一个。
该代码还需要告知已经运行的实例用户试图启动第二个实例,并且还可能传递任何命令行参数(如果存在)。
在.NET(而不是Windows Forms或控制台)下使用C#和WPF ,创建只能作为单个实例运行的应用程序的正确方法是什么?
我知道它与某种称为互斥体的神话事物有关,我很少能找到一个烦人的人来阻止并解释其中的一个。
该代码还需要告知已经运行的实例用户试图启动第二个实例,并且还可能传递任何命令行参数(如果存在)。
Answers:
这是有关Mutex解决方案的非常好的文章。该文章描述的方法有两个方面的优势。
首先,它不需要依赖于Microsoft.VisualBasic程序集。如果我的项目已经依赖于该程序集,那么我可能会提倡使用另一个答案中所示的方法。但实际上,我不使用Microsoft.VisualBasic程序集,而我不想在项目中添加不必要的依赖项。
其次,本文介绍了当用户尝试启动另一个实例时如何将应用程序的现有实例置于前台。这是这里描述的其他Mutex解决方案无法解决的一个很好的方面。
截至2014年8月1日,我上面链接的文章仍处于活动状态,但该博客已有一段时间没有更新。这使我担心,最终它可能会消失,并且随之而来的是所倡导的解决方案。我在此转载本文的内容,以供后代参考。这些单词仅属于Sanity Free Coding的博客所有者。
今天,我想重构一些禁止我的应用程序运行其自身多个实例的代码。
以前,我曾使用System.Diagnostics.Process在进程列表中搜索myapp.exe的实例。尽管此方法有效,但会带来很多开销,我想要更清洁的东西。
知道我可以为此使用互斥锁(但以前从未做过),所以我着手减少代码并简化生活。
static class Program
{
static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
[STAThread]
...
}
拥有一个已命名的互斥锁可以使我们在多个线程和进程之间进行堆栈同步,这正是我要寻找的魔术。
Mutex.WaitOne有一个重载,它指定了我们等待的时间。由于我们实际上并不希望同步代码(更多的是检查当前是否在使用它),我们将重载与两个参数一起使用:Mutex.WaitOne(Timespan timeout,bool exitContext)。如果可以输入,则等待返回true,否则返回false。在这种情况下,我们根本不想等待;如果正在使用我们的互斥锁,请跳过它并继续前进,这样我们传入TimeSpan.Zero(等待0毫秒),并将exitContext设置为true,以便在尝试获取对其的锁定之前可以退出同步上下文。使用此代码,我们将Application.Run代码包装在如下代码中:
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通知运行中的实例有人忘记了它已经在运行(通过将自身置于所有其他窗口的顶部)。为此,我使用PostMessage将自定义消息广播到每个窗口(该自定义消息 已由运行的应用程序注册到RegisterWindowMessage中,这意味着只有我的应用程序知道它是什么),然后退出第二个实例。正在运行的应用程序实例将接收该通知并进行处理。为了做到这一点,我超越了以主要形式 WndProc,并收听了自定义通知。当我收到该通知时,我将表单的TopMost属性设置为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;
}
}
Mutex
构造函数只需要一个字符串,因此您可以提供所需的任何字符串名称,例如“ This Is My Mutex”。因为'Mutex'是可用于其他进程的系统对象,所以您通常希望该名称唯一,以免与同一系统上的其他'Mutex'名称冲突。在本文中,看起来很神秘的字符串是“ Guid”。您可以通过调用以编程方式生成此代码System.Guid.NewGuid()
。就本文而言,用户可能是通过Visual Studio生成的,如下所示: msdn.microsoft.com/en-us/library/ms241442(VS.80).aspx
您可以使用Mutex类,但是很快您将发现您将需要实现代码以自己传递参数。好吧,当我阅读Chris Sell的书时,我在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,则无需执行任何操作。我们将使用的类是WindowsFormsApplicationBase,并继承其实例管理器,然后利用属性和事件来处理单个实例。
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方法的调用将其传递给应用程序。
第三,是时候创建我们的EntryPoint了。无需像通常那样更新应用程序,我们将利用SingleInstanceManager。
public class EntryPoint
{
[STAThread]
public static void Main(string[] args)
{
SingleInstanceManager manager = new SingleInstanceManager();
manager.Run(args);
}
}
好吧,我希望您能够了解所有内容并能够使用此实现并将其实现为自己的实现。
从这里。
跨进程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。
MSDN实际上为C#和VB都提供了一个示例应用程序来完全做到这一点:http : //msdn.microsoft.com/zh-cn/library/ms771662(v=VS.90).aspx
开发单实例检测的最常见,最可靠的技术是使用Microsoft .NET Framework远程处理基础结构(System.Remoting)。Microsoft .NET Framework(2.0版)包括WindowsFormsApplicationBase类型,该类型封装了必需的远程处理功能。要将这种类型合并到WPF应用程序中,需要从该类型派生一个类型,并将其用作应用程序静态入口点方法Main和WPF应用程序的Application类型之间的垫片。填充程序检测何时首次启动应用程序以及何时尝试进行后续启动,并让WPF Application Type类型确定控制如何处理启动。
'Another instance of the app is running. Bye'
那么他们将不会是一个非常满意的用户。您只需(在GUI应用程序中)切换到该应用程序并传递提供的参数-否则,如果命令行参数没有意义,则必须弹出可能已最小化的应用程序。该框架已经对此提供了支持-只是有一个白痴将其命名为DLL Microsoft.VisualBasic
,并且没有放入该框架中。Microsoft.ApplicationUtils
或类似的支持。克服它-或打开Reflector。
提示:如果完全按原样使用此方法,并且已经具有包含资源等的App.xaml,那么您也将要对此进行研究。
此代码应转到main方法。在此处查看有关WPF中主要方法的更多信息。
[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;
}
}
方法二
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(...);
}
}
注意:以上方法假定您的进程/应用程序具有唯一的名称。因为它使用进程名称来查找是否存在任何现有处理器。因此,如果您的应用程序有一个非常通用的名称(即:记事本),则上述方法将行不通。
ProcessName
返回可执行文件名减去exe
。如果您创建一个名为“记事本”的应用程序,并且Windows记事本正在运行,它将在您的应用程序运行时检测到它。
好吧,我为此有一个可弃用的类,在大多数情况下都可以轻松使用:
像这样使用它:
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
}
WPF单实例应用程序是一种使用Mutex和IPC东西,并且还将任何命令行参数传递给正在运行的实例的新方法。
代码C#.NET单实例应用程序标记答案的参考,是一个很好的开始。
但是,我发现当已经存在的实例打开一个模式对话框时,无论该对话框是托管对话框(如另一个窗体(如About框))还是非托管对话框(如即使使用标准的.NET类,也可以使用OpenFileDialog。使用原始代码,主窗体已激活,但模态窗体仍处于非活动状态,这看起来很奇怪,此外,用户必须单击它才能继续使用该应用程序。
因此,我已经创建了SingleInstance实用程序类来为Winforms和WPF应用程序自动处理所有这些。
Winforms:
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页面(并确保将其构建操作设置为page以便能够重新定义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);
}
}
}
这是一个允许您拥有一个应用程序实例的示例。加载任何新实例时,它们会将其参数传递给正在运行的主实例。
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);
}
只是有些想法:在某些情况下,仅要求应用程序的一个实例不像某些人所相信的那样是“ lam脚的”。如果一个数据库允许单个用户访问该应用程序的多个实例,那么数据库应用程序等的难度将增加一个数量级(您知道,所有这些操作都会更新在该用户的多个应用程序实例中打开的所有记录机器等)。首先,对于“名称冲突”,不要使用人类易读的名称-而是使用GUID,或者甚至更好的GUID +人类易读的名称。名称冲突的可能性刚刚被忽略,互斥体不在乎正如某人所指出的那样,DOS攻击会很糟糕,但是如果恶意软件人冒了获取互斥量名称并将其合并到其应用程序中的麻烦,无论如何,您几乎都是目标,并且除了保护互斥量名称外,还需要做更多的工作来保护自己。另外,如果使用以下变量:new Mutex(true,“ some GUID plus Name”,出自AIsFirstInstance),则您已经有了关于Mutex是否是第一个实例的指示符。
这个看似简单的问题有这么多答案。我只是想在这里稍作改动,这就是我对这个问题的解决方案。
创建Mutex可能会很麻烦,因为JIT-er仅看到您将其用于代码的一小部分,并希望将其标记为可进行垃圾回收。它非常想让您以为您不会再使用该Mutex这么长的时间了。实际上,只要您的应用程序运行,您就希望挂在此Mutex上。告诉垃圾收集器让您自己离开Mutex的最好方法是告诉它,使其在不同年代的车库收集中保持活力。例:
var m = new Mutex(...);
...
GC.KeepAlive(m);
我从以下页面提出了这个想法:http : //www.ai.uga.edu/~mc/SingleInstance.html
以下代码是我的WCF命名管道解决方案,用于注册单实例应用程序。很好,因为当另一个实例尝试启动时,它还会引发一个事件,并接收另一个实例的命令行。
它面向WPF,因为它使用System.Windows.StartupEventHandler
该类,但是可以轻松对其进行修改。
此代码需要引用PresentationFramework
和System.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);
}
}
}
}
看下面的代码。这是防止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();
}
}
这是我用的。它结合了过程枚举来执行切换和互斥,以防止“活动的点击器”:
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();
}
}
我找到了更简单的解决方案,类似于Dale Ragan的解决方案,但略有修改。它实际上基于标准的Microsoft WindowsFormsApplicationBase类,可以满足您的所有需求。
首先,创建SingleInstanceController类,该类可在所有其他使用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。如果您只想在用户尝试重新启动正在运行的程序时将正常运行的应用程序重新激活为正常窗口,则SingleInstanceController中的第二个参数可以为null。在给定的示例中,窗口被最大化。
更新2017年1月25日。在尝试了几件事之后,我决定使用VisualBasic.dll,它更容易且效果更好(至少对我而言)。我让以前的回答作为参考...
就像参考一样,这是我不传递参数的方式(我找不到任何理由……我的意思是单个应用程序的参数要从一个实例传递到另一个实例)。如果需要文件关联,则应(按用户标准期望)为每个文档实例化一个应用程序。如果您必须将args传递到现有应用程序,我想我会使用vb dll。
不传递args(仅单实例应用程序),我更喜欢不注册新的Window消息,并且不覆盖Matt Davis解决方案中定义的消息循环。尽管添加VisualBasic dll没什么大不了的,但是我宁愿不要仅仅为了做单实例应用而添加新的引用。此外,我确实更喜欢使用Main实例化一个新类,而不是从App.Startup重写调用Shutdown以确保尽快退出。
希望任何人都会喜欢它...或会有所启发:-)
项目启动类应设置为“ SingleInstanceApp”。
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);
}
}
}
}
}
}
}
WindowHelper:
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);
虽然不使用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
-statement并将“应用程序已在运行”。
这可能对某人有帮助。
基于命名互斥的方法不是跨平台的,因为命名互斥在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.");
}
}
}
}
我在解决方案中使用Mutex来防止多个实例。
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();
}
使用互斥锁解决方案:
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的末尾,它也会被自动丢弃。
您还可以使用CodeFluent Runtime,它是一组免费工具。它提供了SingleInstance类来实现单个实例应用程序。
我向NativeMethods类添加了sendMessage方法。
显然,如果任务栏中未显示该应用程序,则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);
}
这是通过Event实现的同一件事。
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();
}
}
[我在下面提供了控制台和wpf应用程序的示例代码。]
您只需要检查 createdNew
创建命名的Mutex实例后,变量(下面的示例!)。
布尔值createdNew
将返回false:
如果系统上某处已经创建了名为“ YourApplicationNameHere”的互斥对象实例
布尔值createdNew
将返回true:
如果这是系统上第一个名为“ YourApplicationNameHere”的互斥对象。
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#Winforms的省时解决方案...
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);
}
}
}
请从此处检查提出的解决方案,该解决方案使用信号量确定现有实例是否已在运行,是否适用于WPF应用程序,并且可以使用TcpListener和TcpClient将参数从第二个实例传递到第一个已经运行的实例:
它也适用于.NET Core,不仅适用于.NET Framework。
我在这里找不到一个简短的解决方案,所以我希望有人会喜欢这样:
更新于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());
}