c# visual Qual è il modo corretto di creare un'applicazione WPF a istanza singola?




wpf vb (24)

Usando C # e WPF sotto .NET (piuttosto che Windows Form o console), qual è il modo corretto di creare un'applicazione che può essere eseguita solo come una singola istanza?

So che ha qualcosa a che fare con qualcosa di mitico chiamato mutex, raramente riesco a trovare qualcuno che si preoccupa di fermarsi e spiegare che cos'è uno di questi.

Il codice deve anche informare l'istanza già in esecuzione che l'utente ha provato ad avviarne una seconda, e forse anche passare qualsiasi argomento da riga di comando, se esistente.


Da here

Un uso comune per un mutex cross-process è quello di garantire che possa essere eseguita solo l'istanza di un programma alla volta. Ecco come è fatto:

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();
    }    
  }    
}

Una buona caratteristica di Mutex è che se l'applicazione termina senza che prima venga chiamato ReleaseMutex, il CLR rilascerà automaticamente il Mutex.


Normalmente, questo è il codice che utilizzo per le applicazioni Windows Form a istanza singola :

[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());
        }
    }
}

Dove i componenti nativi sono:

[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
}

Aggiornamento 2017-01-25. Dopo aver provato alcune cose, ho deciso di utilizzare VisualBasic.dll, è più semplice e funziona meglio (almeno per me). Lascio la mia risposta precedente proprio come riferimento ...

Proprio come riferimento, questo è il modo in cui ho fatto senza passare argomenti (che non riesco a trovare alcuna ragione per farlo ... Intendo una singola app con argomenti che devono essere passati da un'istanza all'altra). Se è richiesta un'associazione di file, un'app dovrebbe (per le aspettative standard degli utenti) essere instanciata per ciascun documento. Se devi passare argomenti all'app esistente, penso che avrei usato vb dll.

Non passando args (solo app a istanza singola), preferisco non registrare un nuovo messaggio Window e non sovrascrivere il loop del messaggio come definito in Matt Davis Solution. Anche se non è un grosso problema aggiungere una dll VisualBasic, ma preferisco non aggiungere un nuovo riferimento solo per fare un'app a istanza singola. Inoltre, preferisco installare una nuova classe con Main invece di chiamare Shutdown da App.Startup override per assicurarmi di uscire il prima possibile.

Nella speranza che a qualcuno piacerà ... o ispirerà un po ':-)

La classe di avvio del progetto deve essere impostata come "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);

Il codice article che è il riferimento per la risposta contrassegnata è un ottimo inizio.

Tuttavia, ho trovato che non gestisce molto bene i casi in cui l'istanza già esistente ha una finestra di dialogo modale aperta, sia che quella finestra di dialogo sia gestita (come un'altra Form come una scatola di informazioni), o una non gestita (come la OpenFileDialog anche quando si utilizza la classe .NET standard). Con il codice originale, il modulo principale è attivato, ma quello modale rimane non attivo, il che sembra strano, inoltre l'utente deve fare clic su di esso per continuare a utilizzare l'app.

Quindi, ho creato una classe di utilità SingleInstance per gestire tutto questo in modo abbastanza automatico per le applicazioni Winform e WPF.

Winforms :

1) modifica la classe Program in questo modo:

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) modifica la classe della finestra principale in questo modo:

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) modifica la pagina dell'app in questo modo (e assicurati di impostare la sua azione build sulla pagina per poter ridefinire il metodo 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) modifica la classe della finestra principale in questo modo:

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;
    }

Ed ecco la classe di utilità:

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);
        }
    }
}

Di solito ogni volta che eseguiamo un .exe, ogni volta che crea un processo Windows separato con il proprio spazio di indirizzamento, risorse e così via. Ma non vogliamo questo criterio in quanto ciò ci impedirebbe di creare un singolo processo. Le applicazioni a istanza singola possono essere create utilizzando il Mutex in C # che è discusso in questo articolo

Inoltre, se vogliamo portare l'applicazione in cima, possiamo farlo usando

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

Bene, ho una classe usa e getta per questo che funziona facilmente per la maggior parte dei casi d'uso:

Usalo in questo modo:

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

        // Initialize program here.
    }
}

Ecco qui:

/// <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
}

Usa la soluzione mutex:

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;
            }
        }
    }
}
}

Ecco un ottimo article riguardante la soluzione Mutex. L'approccio descritto dall'articolo è vantaggioso per due ragioni.

Innanzitutto, non richiede una dipendenza dall'assembly Microsoft.VisualBasic. Se il mio progetto avesse già una dipendenza da quell'assemblaggio, probabilmente sosterrei l'utilizzo dell'approccio mostrato nella risposta accettata. Ma così com'è, non uso l'assembly Microsoft.VisualBasic e preferisco non aggiungere una dipendenza non necessaria al mio progetto.

In secondo luogo, l'articolo mostra come portare l'istanza esistente dell'applicazione in primo piano quando l'utente tenta di avviare un'altra istanza. Questo è un bel tocco che le altre soluzioni Mutex descritte qui non affrontano.

AGGIORNARE

A partire dall'8 / 01/2014, l'articolo che ho collegato sopra è ancora attivo, ma il blog non è stato aggiornato da un po 'di tempo. Questo mi fa preoccupare che alla fine potrebbe scomparire, e con esso, la soluzione sostenuta. Sto riproducendo il contenuto dell'articolo qui per i posteri. Le parole appartengono esclusivamente al proprietario del blog di Sanity Free Coding .

Oggi volevo refactoring un codice che vietava alla mia applicazione di eseguire più istanze di se stesso.

In precedenza avevo utilizzato System.Diagnostics.Process per cercare un'istanza di myapp.exe nell'elenco dei processi. Mentre questo funziona, porta un sacco di spese generali, e volevo qualcosa di più pulito.

Sapendo che avrei potuto usare un mutex per questo (ma non l'ho mai fatto prima) ho deciso di ridurre il mio codice e semplificare la mia vita.

Nella classe principale della mia applicazione ho creato uno statico chiamato Mutex :

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

Avere un mutex denominato ci consente di impilare la sincronizzazione su più thread e processi, che è proprio la magia che sto cercando.

Mutex.WaitOne ha un sovraccarico che specifica una quantità di tempo che dobbiamo attendere. Dato che in realtà non vogliamo sincronizzare il nostro codice (più semplicemente controlliamo se è attualmente in uso) usiamo il sovraccarico con due parametri: Mutex.WaitOne (Timeout timeout, bool exitContext) . L'attesa restituisce true se è in grado di entrare e false se non lo è. In questo caso, non vogliamo assolutamente aspettare; Se si utilizza il mutex, ignorarlo e andare avanti, quindi passiamo in TimeSpan.Zero (attendere 0 millisecondi) e impostare exitContext su true in modo da poter uscire dal contesto di sincronizzazione prima di provare ad acquisire un blocco su di esso. Usando questo, avvolgiamo il nostro codice Application.Run all'interno di qualcosa del genere:

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");
        }
    }
}

Quindi, se la nostra app è in esecuzione, WaitOne restituirà false e verrà visualizzata una finestra di messaggio.

Invece di mostrare una finestra di messaggio, ho deciso di utilizzare un piccolo Win32 per notificare alla mia istanza in esecuzione che qualcuno ha dimenticato che era già in esecuzione (portandosi in cima a tutte le altre finestre). Per raggiungere questo obiettivo ho usato PostMessage per trasmettere un messaggio personalizzato ad ogni finestra (il messaggio personalizzato è stato registrato con RegisterWindowMessage dalla mia applicazione in esecuzione, il che significa che solo la mia applicazione sa cosa sia), quindi la mia seconda istanza termina. L'istanza dell'applicazione in esecuzione riceverebbe tale notifica ed elaborarla. Per fare ciò, ho annullato WndProc nella mia forma principale e ho ascoltato la mia notifica personalizzata. Quando ho ricevuto tale notifica, ho impostato la proprietà TopMost del modulo su true per portarlo in primo piano.

Ecco cosa ho finito con:

  • 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 (parte anteriore parziale)
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;
    }
}

Ho aggiunto un metodo sendMessage alla classe NativeMethods.

Apparentemente il lavoro di dosaggio del metodo postmessage, se l'applicazione non è visualizzata nella barra delle applicazioni, tuttavia l'utilizzo del metodo sendmessage risolve questo problema.

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);
}

Non si dovrebbe mai usare un mutex denominato per implementare un'applicazione a istanza singola (o almeno non per codice di produzione). Il codice dannoso può facilmente fare il tuo asino ( Denial of Service ) ...


Così tante risposte a una domanda apparentemente semplice. Solo per scuotere un po 'le cose qui è la mia soluzione a questo problema.

La creazione di un Mutex può essere problematica perché il JIT-er vede solo che la usi per una piccola parte del tuo codice e vuole contrassegnarlo come pronto per la garbage collection. Vuole praticamente pensare che non si userà quel Mutex per così tanto tempo. In realtà ti vuoi aggrappare a questo Mutex per tutto il tempo che la tua applicazione è in esecuzione. Il modo migliore per dire al garbage collector di lasciarti Mutex da solo è dirgli di tenerlo in vita attraverso le diverse generazioni di garage collection. Esempio:

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

Ho sollevato l'idea da questa pagina: http://www.ai.uga.edu/~mc/SingleInstance.html


Tuttavia, non usando Mutex, risposta semplice:

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

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

Inseriscilo nel Program.Main().
Esempio :

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());
        }
    }
}

È possibile aggiungere MessageBox.Showa if-statement e mettere "Applicazione già in esecuzione".
Questo potrebbe essere utile a qualcuno.


Sembra che ci sia un ottimo modo per gestire questo:

blogs.microsoft.co.il/blogs/arik/archive/2010/05/28/…

Questo fornisce una classe che puoi aggiungere che gestisce tutto il mutex e la messaggistica cruff per semplificare la tua implementazione fino al punto in cui è semplicemente banale.


Ecco un esempio che ti consente di avere una singola istanza di un'applicazione. Quando vengono caricate nuove istanze, passano i loro argomenti all'istanza principale in esecuzione.

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);
}

Ecco i miei 2 centesimi

 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;
                    }
                }
            }
        }
    }

Ecco la stessa cosa implementata tramite 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();
    }
}

Potresti usare la classe Mutex, ma presto scoprirai che dovrai implementare il codice per passare gli argomenti e te stesso. Bene, ho imparato un trucco durante la programmazione in WinForms quando ho letto il libro di Chris Sell . Questo trucco utilizza la logica che è già disponibile per noi nel framework. Non so voi, ma quando apprendo cose che posso riutilizzare nel framework, di solito è la strada che faccio invece di reinventare la ruota. A meno che, naturalmente, non faccia tutto ciò che voglio.

Quando sono entrato in WPF, ho trovato un modo per usare lo stesso codice, ma in un'applicazione WPF. Questa soluzione dovrebbe soddisfare le tue esigenze in base alla tua domanda.

Per prima cosa, dobbiamo creare la nostra classe di applicazione. In questo corso stiamo ignorando l'evento OnStartup e creiamo un metodo chiamato Activate, che verrà utilizzato in seguito.

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();
    }
}

Secondo, dovremo creare una classe in grado di gestire le nostre istanze. Prima di passare a questo, in realtà stiamo per riutilizzare un codice nell'assembly Microsoft.VisualBasic. Dato che, in questo esempio, sto usando C #, ho dovuto fare riferimento all'assembly. Se stai usando VB.NET, non devi fare nulla. La classe che useremo è WindowsFormsApplicationBase e ereditiamo il nostro gestore di istanze al di fuori di esso e quindi sfruttiamo proprietà ed eventi per gestire l'istanza singola.

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();
    }
}

Fondamentalmente, stiamo usando i bit VB per rilevare le singole istanze e processare di conseguenza. OnStartup verrà generato quando viene caricata la prima istanza. OnStartupNextInstance viene generato quando l'applicazione viene rieseguita di nuovo. Come puoi vedere, posso arrivare a ciò che è stato passato sulla linea di comando attraverso gli argomenti dell'evento. Ho impostato il valore su un campo di istanza. Potresti analizzare la riga di comando qui, oppure puoi passarla alla tua applicazione attraverso il costruttore e la chiamata al metodo Activate.

Terzo, è il momento di creare il nostro EntryPoint. Invece di iniziare l'applicazione come faresti normalmente, sfrutteremo il nostro SingleInstanceManager.

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

Bene, spero che tu sia in grado di seguire tutto ed essere in grado di utilizzare questa implementazione e renderla tua.


Questo codice dovrebbe andare al metodo principale. Guarda here per ulteriori informazioni sul metodo principale in 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; 
    }
}

Metodo 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(...);
    }
}

Nota: i metodi sopra riportati presuppongono che il processo / l'applicazione abbia un nome univoco. Perché utilizza il nome del processo per trovare eventuali processori esistenti. Quindi, se la tua applicazione ha un nome molto comune (es .: Blocco note), l'approccio sopra non funzionerà.


Ho trovato la soluzione più semplice, simile a quella di Dale Ragan, ma leggermente modificata. Praticamente praticamente tutto ciò che serve e basato sulla classe standard Microsoft WindowsFormsApplicationBase.

In primo luogo, si crea la classe SingleInstanceController, che è possibile utilizzare in tutte le altre applicazioni a istanza singola, che utilizzano Windows Form:

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);
        }
    }
}

Quindi puoi usarlo nel tuo programma come segue:

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();         
        }
    }
}

Sia il programma che la soluzione SingleInstanceController_NET ​​devono fare riferimento a Microsoft.VisualBasic. Se si desidera riattivare l'applicazione in esecuzione come una finestra normale quando l'utente tenta di riavviare il programma in esecuzione, il secondo parametro in SingleInstanceController può essere nullo. Nell'esempio indicato, la finestra è ingrandita.


MSDN ha effettivamente un'applicazione di esempio per C # e VB per fare esattamente questo: http://msdn.microsoft.com/en-us/library/ms771662(v=VS.90).aspx

La tecnica più comune e affidabile per lo sviluppo del rilevamento a istanza singola consiste nell'utilizzare l'infrastruttura remota di Microsoft .NET Framework (System.Remoting). Microsoft .NET Framework (versione 2.0) include un tipo, WindowsFormsApplicationBase, che incapsula la funzionalità di comunicazione remota richiesta. Per incorporare questo tipo in un'applicazione WPF, è necessario derivarne un tipo e utilizzarlo come shim tra il metodo del punto di ingresso statico dell'applicazione, Main e il tipo di applicazione dell'applicazione WPF. Lo shim rileva quando viene lanciata un'applicazione per la prima volta e quando vengono tentati lanci successivi e produce il controllo del tipo di applicazione WPF per determinare come elaborare i lanci.

  • Per le persone di C # basta fare un respiro profondo e dimenticare l'intero 'Io non voglio includere VisualBasic DLL'. Per this motivo e quello che Scott Hanselman dice e il fatto che questo è praticamente la soluzione più pulita al problema ed è progettato da persone che conoscono molto di più del framework rispetto a ciò che fanno.
  • Dal punto di vista dell'usabilità, il fatto è che l'utente stia caricando un'applicazione ed è già aperta e gli stai dando un messaggio di errore come 'Another instance of the app is running. Bye' 'Another instance of the app is running. Bye' allora non saranno un utente molto felice. Devi semplicemente (in un'applicazione GUI) passare a quell'applicazione e passare gli argomenti forniti, oppure se i parametri della riga di comando non hanno alcun significato, devi far apparire l'applicazione che potrebbe essere stata ridotta a icona.

Il framework ha già il supporto per questo - è solo che un idiota ha chiamato la DLL Microsoft.VisualBasic e non è stato inserito in Microsoft.ApplicationUtils o qualcosa del genere. Passaci sopra o apri Reflector.

Suggerimento: se si utilizza questo approccio esattamente come è, e si dispone già di un'app.xaml con risorse ecc., Si consiglia di dare un'occhiata anche a questo .



Guarda il codice seguente. Si tratta di un'ottima e semplice soluzione per prevenire più istanze di un'applicazione 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();
    }
}

Ecco una soluzione:

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

Solo alcuni pensieri: ci sono casi in cui si richiede che solo una istanza di un'applicazione non sia "zoppa" come qualcuno vorrebbe credere. Le app di database, ecc. Sono un ordine di grandezza più difficile se si consentono più istanze dell'app per un singolo utente di accedere a un database (lo sai, tutto ciò che aggiorna tutti i record che sono aperti in più istanze dell'app sugli utenti macchina, ecc.). Innanzitutto, per il nome "collision thing", non utilizzare un nome leggibile dall'uomo, utilizzare invece un GUID o, ancora meglio, un GUID + il nome leggibile dall'uomo. Le probabilità di collisione di nomi sono appena uscite dal radar e al Mutex non importa Come qualcuno ha sottolineato, un attacco DOS farebbe schifo, ma se la persona malintenzionata si è presa la briga di ottenere il nome mutex e di inserirlo nella sua app,sei praticamente un bersaglio comunque e dovresti fare MOLTO di più per proteggerti piuttosto che armeggiare un nome mutex. Inoltre, se si utilizza la variante di: new Mutex (true, "some GUID plus Name", out AIsFirstInstance), si dispone già dell'indicatore sul fatto che Mutex sia la prima istanza o meno.







mutex