Frage

Verwendung von C# und WPF unter .NET (statt Windows Forms oder Konsole), wie erstellt man eine Anwendung richtig, die nur als einzelne Instanz ausgeführt werden kann?

Ich weiß, dass es etwas mit einem mythischen Ding namens Mutex zu tun hat. Selten finde ich jemanden, der sich die Mühe macht, anzuhalten und zu erklären, was das ist.

Der Code muss auch die bereits laufende Instanz darüber informieren, dass der Benutzer versucht hat, eine zweite zu starten, und möglicherweise auch Befehlszeilenargumente übergeben, falls vorhanden.

War es hilfreich?

Lösung

Hier ist ein sehr gutes Artikel bezüglich der Mutex-Lösung.Der im Artikel beschriebene Ansatz ist aus zwei Gründen vorteilhaft.

Erstens ist keine Abhängigkeit von der Microsoft.VisualBasic-Assembly erforderlich.Wenn mein Projekt bereits eine Abhängigkeit von dieser Assembly hätte, würde ich wahrscheinlich die Verwendung dieses Ansatzes befürworten in einer anderen Antwort gezeigt.Aber so wie es ist, verwende ich nicht die Microsoft.VisualBasic-Assembly und möchte meinem Projekt lieber keine unnötige Abhängigkeit hinzufügen.

Zweitens zeigt der Artikel, wie man die vorhandene Instanz der Anwendung in den Vordergrund bringt, wenn der Benutzer versucht, eine andere Instanz zu starten.Das ist eine sehr nette Geste, auf die die anderen hier beschriebenen Mutex-Lösungen nicht eingehen.


AKTUALISIEREN

Mit Stand vom 01.08.2014 ist der Artikel, auf den ich oben verlinkt habe, immer noch aktiv, aber der Blog wurde schon seit einiger Zeit nicht mehr aktualisiert.Das macht mir Sorgen, dass es irgendwann verschwinden könnte und damit auch die befürwortete Lösung.Ich gebe den Inhalt des Artikels hier für die Nachwelt wieder.Die Worte gehören ausschließlich dem Blog-Inhaber Sanitätsfreie Codierung.

Heute wollte ich einen Code neu umarbeiten, der meine Anwendung untersagte, mehrere Instanzen von sich selbst auszuführen.

Zuvor hatte ich Gebrauch System.Diagnostics.Process Suche nach einer Instanz meines MyApp.exe in der Prozessliste.Während dies funktioniert, bringt es viel Overhead auf, und ich wollte etwas Reinigerer.

Da ich weiß, dass ich dafür einen Mutex verwenden konnte (aber noch nie zuvor getan habe), machte ich mich daran, meinen Code zu senken und mein Leben zu vereinfachen.

In der Klasse meiner Anwendung main habe ich eine statische Datei mit dem Namen erstellt Mutex:

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

Mit einem benannten Mutex können wir die Synchronisation über mehrere Threads und Prozesse hinweg stapeln, was nur die Magie ist, nach der ich suche.

Mutex.WaitOne hat eine Überlastung, die eine Zeitspanne für uns zum Warten angibt.Da wir unseren Code nicht synchronisieren möchten (mehr prüfen Sie einfach, ob er derzeit verwendet wird), verwenden wir die Überlastung mit zwei Parametern: Mutex.WaitOne(Timespan Timeout, Bool ExitContext).Warten Sie, es wird „true“ zurückgegeben, wenn die Eingabe möglich ist, und „false“, wenn dies nicht der Fall ist.In diesem Fall wollen wir gar nicht warten;Wenn unser Mutex verwendet wird, überspringen Sie es und fahren Sie fort, so dass wir Timesspan.zero (Warten Sie 0 Millisekunden) übergeben und den ExitContext auf True setzen, damit wir den Kontext der Synchronisation verlassen können, bevor wir versuchen, eine Sperre darauf zu lockern.Mit dieser Weise wickeln wir unseren Code in so etwas ein.

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

Wenn unsere App ausgeführt wird, wird WaitOne falsch zurückgegeben und wir erhalten ein Nachrichtenfeld.

Anstatt ein Nachrichtenfeld anzuzeigen, habe ich mich entschieden, ein kleines Win32 zu verwenden, um meine Laufinstanz darüber zu informieren, dass jemand vergessen hat, dass es bereits ausgeführt wurde (indem sie sich ganz oben auf alle anderen Fenster brachte).Um dies zu erreichen, habe ich verwendet POST-Meldung um eine benutzerdefinierte Nachricht an jedes Fenster zu übertragen (die benutzerdefinierte Nachricht wurde mit registriert RegisterWindowMessageBei meiner laufenden Anwendung, was bedeutet, dass nur meine Bewerbung weiß, was sie ist) dann beendet meine zweite Instanz.Die laufende Anwendungsinstanz würde diese Benachrichtigung erhalten und verarbeiten.Um das zu tun, habe ich überschreiben WndProc in meinem Hauptformular und zuhörte für meine benutzerdefinierte Benachrichtigung.Als ich diese Benachrichtigung erhielt, setzte ich die oberste Eigenschaft des Formulars auf True, um es oben zu bringen.

Hier ist, was ich am Ende herausgefunden habe:

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

Andere Tipps

Sie könnten die Mutex-Klasse verwenden, aber Sie werden bald feststellen, dass Sie den Code zur Übergabe der Argumente usw. selbst implementieren müssen.Nun, ich habe beim Lesen einen Trick beim Programmieren in WinForms gelernt Chris Sells Buch.Dieser Trick nutzt Logik, die uns bereits im Framework zur Verfügung steht.Ich weiß nicht, wie es Ihnen geht, aber wenn ich etwas über Dinge erfahre, die ich im Framework wiederverwenden kann, ist das normalerweise der Weg, den ich einschlage, anstatt das Rad neu zu erfinden.Es sei denn natürlich, es macht nicht alles, was ich will.

Als ich mit WPF angefangen habe, habe ich eine Möglichkeit gefunden, denselben Code zu verwenden, aber in einer WPF-Anwendung.Diese Lösung sollte Ihren Anforderungen basierend auf Ihrer Frage entsprechen.

Zuerst müssen wir unsere Anwendungsklasse erstellen.In dieser Klasse überschreiben wir das OnStartup-Ereignis und erstellen eine Methode namens Activate, die später verwendet wird.

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

Zweitens müssen wir eine Klasse erstellen, die unsere Instanzen verwalten kann.Bevor wir darauf eingehen, werden wir tatsächlich Code aus der Microsoft.VisualBasic-Assembly wiederverwenden.Da ich in diesem Beispiel C# verwende, musste ich einen Verweis auf die Assembly erstellen.Wenn Sie VB.NET verwenden, müssen Sie nichts tun.Die Klasse, die wir verwenden werden, ist WindowsFormsApplicationBase und erbt davon unseren Instanzmanager und nutzt dann Eigenschaften und Ereignisse, um die einzelne Instanziierung zu handhaben.

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

Grundsätzlich verwenden wir die VB-Bits, um einzelne Instanzen zu erkennen und entsprechend zu verarbeiten.OnStartup wird ausgelöst, wenn die erste Instanz geladen wird.OnStartupNextInstance wird ausgelöst, wenn die Anwendung erneut ausgeführt wird.Wie Sie sehen, kann ich über die Ereignisargumente herausfinden, was in der Befehlszeile übergeben wurde.Ich habe den Wert auf ein Instanzfeld gesetzt.Sie können die Befehlszeile hier analysieren oder sie über den Konstruktor und den Aufruf der Activate-Methode an Ihre Anwendung übergeben.

Drittens ist es an der Zeit, unseren EntryPoint zu erstellen.Anstatt die Anwendung wie gewohnt zu aktualisieren, nutzen wir unseren SingleInstanceManager.

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

Nun, ich hoffe, Sie können alles verfolgen und diese Implementierung nutzen und zu Ihrer eigenen machen.

Aus Hier.

Ein prozessübergreifender Mutex wird häufig verwendet, um sicherzustellen, dass jeweils nur eine Instanz eines Programms ausgeführt werden kann.So wird es gemacht:

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

Eine gute Funktion von Mutex besteht darin, dass die CLR den Mutex automatisch freigibt, wenn die Anwendung beendet wird, ohne dass zuerst ReleaseMutex aufgerufen wurde.

MSDN verfügt tatsächlich über eine Beispielanwendung für C# und VB, um genau dies zu tun: http://msdn.microsoft.com/en-us/library/ms771662(v=VS.90).aspx

Die häufigste und zuverlässigste Technik zur Entwicklung einer Einzelinstanz-Erkennung besteht darin, die Remoting-Infrastruktur von Microsoft .NET Framework (System.Remoting) zu verwenden.Das Microsoft .NET -Framework (Version 2.0) enthält einen Typ, WindowsFormsApplicationBase, der die erforderliche Remoting -Funktionen zusammenfasst.Um diesen Typ in eine WPF -Anwendung einzubeziehen, muss ein Typ daraus abgeleitet und als Scheibe zwischen der Anwendung statischer Einstiegspunktmethode und dem Anwendungstyp der WPF -Anwendung verwendet werden.Der Shim erkennt, wann eine Anwendung erstmals gestartet wird und wann nachfolgende Starts versucht wird, und steuert den WPF -Anwendungstyp, um zu bestimmen, wie die Starts verarbeitet werden.

  • C#-Leute atmen einfach tief durch und vergessen das ganze „Ich möchte keine VisualBasic-DLL einbinden“.Wegen Das und was Scott Hanselman sagt und die Tatsache, dass dies so ziemlich die sauberste Lösung des Problems ist und von Leuten entworfen wurde, die viel mehr über das Framework wissen als Sie.
  • Unter dem Gesichtspunkt der Benutzerfreundlichkeit gilt Folgendes: Wenn Ihr Benutzer eine Anwendung lädt und diese bereits geöffnet ist, geben Sie ihm eine Fehlermeldung wie „ 'Another instance of the app is running. Bye' dann werden sie keine sehr glücklichen Benutzer sein.Sie MÜSSEN einfach (in einer GUI-Anwendung) zu dieser Anwendung wechseln und die bereitgestellten Argumente übergeben – oder wenn Befehlszeilenparameter keine Bedeutung haben, müssen Sie die Anwendung öffnen, die möglicherweise minimiert wurde.

Das Framework unterstützt dies bereits – es ist nur so, dass irgendein Idiot die DLL benannt hat Microsoft.VisualBasic und es wurde nicht eingebaut Microsoft.ApplicationUtils oder etwas ähnliches.Überwinde es – oder öffne Reflector.

Tipp:Wenn Sie diesen Ansatz genau so verwenden, wie er ist, und Sie bereits über eine App.xaml mit Ressourcen usw. verfügen.Du wirst es wollen schau dir das auch mal an.

Dieser Code sollte zur Hauptmethode gehen.Ansehen Hier Weitere Informationen zur Hauptmethode in WPF finden Sie hier.

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

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

Notiz : Bei den oben genannten Methoden wird davon ausgegangen, dass Ihr Prozess/Ihre Anwendung einen eindeutigen Namen hat.Weil es den Prozessnamen verwendet, um herauszufinden, ob Prozessoren vorhanden sind.Wenn Ihre Anwendung also einen sehr gebräuchlichen Namen hat (z. B.:Notepad) funktioniert der obige Ansatz nicht.

Nun, ich habe dafür eine Einwegklasse, die für die meisten Anwendungsfälle problemlos funktioniert:

Benutze es so:

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

        // Initialize program here.
    }
}

Hier ist es:

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

Eine neue Version, die Mutex- und IPC-Inhalte verwendet und außerdem alle Befehlszeilenargumente an die laufende Instanz übergibt, ist WPF-Einzelinstanzanwendung.

Der Code C# .NET-Einzelinstanzanwendung Das ist die Referenz für die markierte Antwort. Das ist ein guter Anfang.

Ich habe jedoch festgestellt, dass es die Fälle, in denen in der bereits vorhandenen Instanz ein modales Dialogfeld geöffnet ist, nicht besonders gut handhabt, unabhängig davon, ob es sich bei diesem Dialogfeld um ein verwaltetes (wie ein anderes Formular, z. B. ein About-Feld) oder ein nicht verwaltetes (wie das) handelt OpenFileDialog auch bei Verwendung der Standard-.NET-Klasse).Mit dem Originalcode wird das Hauptformular aktiviert, das modale bleibt jedoch inaktiv, was seltsam aussieht, und der Benutzer muss darauf klicken, um die App weiterhin verwenden zu können.

Deshalb habe ich eine SingleInstance-Dienstprogrammklasse erstellt, um all dies für Winforms- und WPF-Anwendungen ganz automatisch zu erledigen.

Winforms:

1) Ändern Sie die Programmklasse wie folgt:

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) Ändern Sie die Hauptfensterklasse wie folgt:

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) Ändern Sie die App-Seite wie folgt (und stellen Sie sicher, dass Sie ihre Build-Aktion auf Seite setzen, um die Main-Methode neu definieren zu können):

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) Ändern Sie die Hauptfensterklasse wie folgt:

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

Und hier ist die Utility-Klasse:

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

Hier ist ein Beispiel, das es Ihnen ermöglicht, eine einzelne Instanz einer Anwendung zu haben.Wenn neue Instanzen geladen werden, übergeben sie ihre Argumente an die laufende Hauptinstanz.

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

Nur ein paar Gedanken:Es gibt Fälle, in denen gefordert wird, dass nur eine Instanz einer Anwendung nicht „lahm“ ist, wie manche glauben machen wollen.Datenbank-Apps usw.sind um eine Größenordnung schwieriger, wenn man mehreren Instanzen der App für einen einzelnen Benutzer den Zugriff auf eine Datenbank ermöglicht (Sie wissen schon, all das Aktualisieren aller Datensätze, die in mehreren Instanzen der App auf dem Computer des Benutzers geöffnet sind usw.) .Erstens: Verwenden Sie für die Namenskollision keinen für Menschen lesbaren Namen, sondern stattdessen eine GUID oder, noch besser, eine GUID + den für Menschen lesbaren Namen.Die Wahrscheinlichkeit einer Namenskollision ist gerade von der Bildfläche verschwunden und dem Mutex ist das egal.Wie jemand betonte, wäre ein DOS-Angriff scheiße, aber wenn sich die böswillige Person die Mühe gemacht hat, an den Mutex-Namen zu kommen und ihn in ihre App zu integrieren, sind Sie ohnehin ein ziemliches Ziel und müssen VIEL mehr tun, um sich zu schützen als nur einen Mutex-Namen auszuprobieren.Auch wenn man die Variante verwendet von:new Mutex(true, „some GUID plus Name“, out AIsFirstInstance) haben Sie bereits Ihren Indikator dafür, ob der Mutex die erste Instanz ist oder nicht.

So viele Antworten auf eine so scheinbar einfache Frage.Um die Sache ein wenig aufzumischen, hier ist meine Lösung für dieses Problem.

Das Erstellen eines Mutex kann mühsam sein, da der JIT-Ersteller nur sieht, dass Sie ihn für einen kleinen Teil Ihres Codes verwenden, und ihn als bereit für die Garbage Collection markieren möchte.Es möchte Sie im Grunde überlisten, weil Sie denken, dass Sie diesen Mutex nicht so lange verwenden werden.In Wirklichkeit möchten Sie an diesem Mutex festhalten, solange Ihre Anwendung ausgeführt wird.Der beste Weg, dem Müllsammler mitzuteilen, dass er Mutex in Ruhe lassen soll, besteht darin, ihm mitzuteilen, dass er es über die verschiedenen Generationen der Garagensammlung hinweg am Leben halten soll.Beispiel:

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

Ich habe die Idee von dieser Seite übernommen: http://www.ai.uga.edu/~mc/SingleInstance.html

Es sieht so aus, als gäbe es eine wirklich gute Möglichkeit, damit umzugehen:

WPF-Einzelinstanzanwendung

Dadurch erhalten Sie eine Klasse, die Sie hinzufügen können und die den gesamten Mutex- und Messaging-Schrott verwaltet, um Ihre Implementierung so weit zu vereinfachen, dass sie einfach trivial ist.

Der folgende Code ist meine WCF-Named-Pipes-Lösung zum Registrieren einer Einzelinstanzanwendung.Das ist schön, weil es auch ein Ereignis auslöst, wenn eine andere Instanz zu starten versucht, und die Befehlszeile der anderen Instanz empfängt.

Es ist auf WPF ausgerichtet, da es das verwendet System.Windows.StartupEventHandler Klasse, aber dies könnte leicht geändert werden.

Dieser Code erfordert einen Verweis auf PresentationFramework, Und System.ServiceModel.

Verwendung:

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

Quellcode:

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

Sie sollten niemals einen benannten Mutex verwenden, um eine Einzelinstanzanwendung zu implementieren (oder zumindest nicht für Produktionscode).Schädlicher Code kann leicht zu DoS führen (Denial of Service) Dein Arsch...

Hier ist, was ich verwende.Es kombinierte Prozessaufzählung zum Durchführen von Switches und Mutex zum Schutz vor „aktiven Klickern“:

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

Ich habe die einfachere Lösung gefunden, ähnlich der von Dale Ragan, aber leicht modifiziert.Es macht praktisch alles, was Sie brauchen, und basiert auf der Standardklasse Microsoft WindowsFormsApplicationBase.

Zunächst erstellen Sie die Klasse SingleInstanceController, die Sie in allen anderen Einzelinstanzanwendungen verwenden können, die Windows Forms verwenden:

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

Dann können Sie es wie folgt in Ihrem Programm verwenden:

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

Sowohl das Programm als auch die SingleInstanceController_NET-Lösung sollten auf Microsoft.VisualBasic verweisen.Wenn Sie die laufende Anwendung einfach als normales Fenster reaktivieren möchten, wenn der Benutzer versucht, das laufende Programm neu zu starten, kann der zweite Parameter im SingleInstanceController null sein.Im gegebenen Beispiel ist das Fenster maximiert.

Schauen Sie sich den folgenden Code an.Es ist eine großartige und einfache Lösung, um mehrere Instanzen einer WPF-Anwendung zu verhindern.

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

Mutex wird jedoch nicht verwendet, einfache Antwort:

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

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

Legen Sie es hinein Program.Main().
Beispiel:

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

Du kannst hinzufügen MessageBox.Show zum if-Anweisung und geben Sie „Anwendung wird bereits ausgeführt“ ein.
Das könnte für jemanden hilfreich sein.

Update 25.01.2017. Nachdem ich einige Dinge ausprobiert hatte, entschied ich mich für VisualBasic.dll. Es ist einfacher und funktioniert besser (zumindest für mich).Ich lasse meine vorherige Antwort nur als Referenz dienen ...

Nur als Referenz: So habe ich es gemacht, ohne Argumente zu übergeben (wofür ich keinen Grund sehe ...Ich meine eine einzelne App mit Argumenten, die von einer Instanz an eine andere weitergegeben werden.Wenn eine Dateizuordnung erforderlich ist, sollte (gemäß den Standarderwartungen des Benutzers) für jedes Dokument eine App instanziiert werden.Wenn Sie Argumente an eine vorhandene App übergeben müssen, würde ich wahrscheinlich die VB-DLL verwenden.

Da ich keine Argumente übergebe (nur eine Einzelinstanz-App), ziehe ich es vor, keine neue Fensternachricht zu registrieren und die Nachrichtenschleife nicht zu überschreiben, wie in Matt Davis Solution definiert.Obwohl es keine große Sache ist, eine VisualBasic-DLL hinzuzufügen, ziehe ich es vor, keine neue Referenz hinzuzufügen, nur um eine Einzelinstanz-App zu erstellen.Außerdem instanziiere ich lieber eine neue Klasse mit Main, anstatt Shutdown über App.Startup override aufzurufen, um sicherzustellen, dass die Klasse so schnell wie möglich beendet wird.

In der Hoffnung, dass es irgendjemandem gefällt...oder werde ein wenig inspirieren :-)

Die Startklasse des Projekts sollte auf „SingleInstanceApp“ festgelegt werden.

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-Lösung verwenden:

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

Hier ist eine einfache Lösung, die ich verwende und die es der Anwendung ermöglicht, ein bereits vorhandenes Fenster in den Vordergrund zu bringen, ohne auf benutzerdefinierte Windows-Meldungen zurückzugreifen oder blind nach Prozessnamen zu suchen.

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

Bearbeiten:Sie können Mutex und „createdNew“ auch statisch speichern und initialisieren, müssen den Mutex jedoch explizit entsorgen/freigeben, sobald Sie damit fertig sind.Persönlich bevorzuge ich es, den Mutex lokal zu belassen, da er automatisch gelöscht wird, selbst wenn die Anwendung geschlossen wird, ohne jemals das Ende von Main zu erreichen.

Sie können auch die verwenden CodeFluent-Laufzeit Das ist ein kostenloser Satz an Werkzeugen.Es bietet eine SingleInstance Klasse zum Implementieren einer Einzelinstanzanwendung.

Hier ist das Gleiche, das über Event implementiert wird.

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

Auf benannten Mutexen basierende Ansätze sind nicht plattformübergreifend, da benannte Mutexe in Mono nicht global sind.Auf Prozessaufzählung basierende Ansätze verfügen über keine Synchronisierung und können zu fehlerhaftem Verhalten führen (z. B.(Mehrere Prozesse, die gleichzeitig gestartet werden, können je nach Zeitpunkt alle selbst beendet werden.)Auf einem Fenstersystem basierende Ansätze sind in einer Konsolenanwendung nicht wünschenswert.Diese Lösung, die auf Divins Antwort aufbaut, behebt alle diese Probleme:

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

Ich kann keinen finden kurze Lösung hier, also hoffe ich, dass es jemandem gefällt:

AKTUALISIERT 20.09.2018

(Übrigens.Geben Sie den Code ein „Programm.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;
        }

// Optionaler Code.Wenn Sie nicht möchten, dass jemand Ihre Datei „.exe“ mit einem anderen Namen ausführt:

        string exeName = AppDomain.CurrentDomain.FriendlyName;
        if (exeName != "the name of you're executable.exe") // If you try that in debug mode, don't forget that u don't use ur normal .exe. Debug uses the .vshost.exe.
        {// You can add here a MessageBox if you want. To point 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 you're executable.exe\"", 
            "Wrong executable name", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return;
        }

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

Ich habe der NativeMethods-Klasse eine sendMessage-Methode hinzugefügt.

Anscheinend funktioniert die Postmessage-Methode nicht, wenn die Anwendung nicht in der Taskleiste angezeigt wird. Die Verwendung der Sendmessage-Methode löst dieses Problem jedoch.

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

Normalerweise wird jedes Mal, wenn wir eine .exe-Datei ausführen, ein separater Windows-Prozess mit eigenem Adressraum, eigenen Ressourcen usw. erstellt.Wir möchten dieses Kriterium jedoch nicht, da uns dies daran hindern würde, einen einzelnen Prozess zu erstellen.Einzelinstanzanwendungen können mit dem Mutex in C# erstellt werden wird in diesem Artikel besprochen

Wenn wir die Anwendung darüber hinaus integrieren möchten, können wir dies auch mit tun

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

Normalerweise ist dies der Code, den ich für Einzelinstanzen verwende Windows Forms Anwendungen:

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

Wo native Komponenten sind:

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

Hier ist eine Lösung:

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

So habe ich mich letztendlich um dieses Problem gekümmert.Beachten Sie, dass noch Debug-Code zum Testen vorhanden ist.Dieser Code befindet sich im OnStartup in der Datei App.xaml.cs.(WPF)

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

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

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

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

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

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

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

                }
            }

            Application.Current.Shutdown();

        }

Dies kann Probleme haben, die ich noch nicht erkannt habe.Wenn ich auf welche stoße, werde ich meine Antwort aktualisieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top