Nur eine Programminstanz zulassen

Nur eine Programminstanz zulassen

Oftmals schreibt man Anwendungen, die Datenmanipulationen oder ähnliches durchführen und somit im schlimmsten Fall Kollisionen verursachen würden, wenn diese mehr als einmal zeitgleich ausgeführt werden.
Hierzu ist es sinnvoll, schon beim starten der Anwendung einen Fehler anzuzeigen, wenn die Anwendung ein zweites mal gestartet wird.

Dieser Artikel soll nun anhand eines kurzen Beispiels zeigen, wie ihr das recht einfach umsetzen könnt, dazu teile ich den Artikel in zwei Schritte auf: Im ersten Schritt überprüfen wir lediglich ob es bereits eine Programm-Instanz gibt und im zweiten Schritt gehe ich kurz darauf ein, wie ihr Programmparameter an eine bereits laufende Instanz weiterleiten könnt.

1. Nur eine Instanz zulassen
Zur überprüfung, ob bereits eine Programminstanz läuft, ist die Mutex-Klasse hervorragend geeignet.
Bei MSDN wird die Klasse wie folgt beschrieben: "Ein primitiver Synchronisierungstyp, der auch für die prozessübergreifende Synchronisierung verwendet werden kann.".
D.h. ein Mutex-Objekt kann genutzt werden, um über verschiedene Prozesse (Anwendungen) hinweg, eine Synchronisation durchzuführen.
Das praktische am Mutext ist der letzte Parameter, dieser wird auf true gesetzt, wenn der Mutext neu erstellt wurde, existiert bereits ein Mutext mit diesem Name, so wird der Wert auf false gesetzt.
Wenn wir nun für unsere Anwendung einen eindeutigen Name verwenden und beim Programmstart ein Mutex erzeugen, so können wir dadrüber sehr einfach überprüfen, ob die Anwendung bereits läuft.

Folgender Code demonstriert ein einfaches Beispiel:
static void Main(string[] args)
{
    bool createdNew;
    Mutex mutex = new Mutex(true, "EB943C9A-45ED-40AC-A562-94F3C0FFD730", out createdNew);
    if (createdNew)
    {
        Console.WriteLine("Die Anwendung wurde gestartet.");
    }
    else
    {
        Console.WriteLine("Die Anwendung läuft bereits.");
    }

    Console.ReadKey(false);
}

Als Name habe ich mich für einen GUID entschieden.
Kleiner Tipp: Ihr könnt mit VisualStudio über Tools / Create Guid einen GUID erzeugen.

Der Code sollte soweit selbsterklärend sein.

2. Parameter weitergeben
Dieses Beispiel soll demonstrieren, wie ihr die Programm-Parameter an die bereits laufende Instanz weiterleiten könnt, damit diese dann damit weiterarbeiten kann.
Das ist z.B. dann sinnvoll, wenn eure Anwendung das Windows-Feature "Öffnen mit" Unterstützen soll, also mit einen Dateityp verknüpft ist.
In diesem Fall müsstet ihr ja den Dateipfad von der neuen Instanz an die bereits laufende Instanz weiterleiten.

Wie so oft, führen viele Wege zum Ziel, ich verwende in meinen Beispiel .NET Remoting - IpcChanel dazu.
Diese Technik ermöglicht euch, einen Kanal zu erstellen, über den mehrere Prozesse auf der selben Maschine kommunizieren können.

Folgendes Beispiel demonstriert euch, wie das ganze funktioniert:
private const string GUID_Application = "EB943C9A-45ED-40AC-A562-94F3C0FFD730";
private const string GUID_Channel = "F93A69BD-2F45-4C30-9BEC-266BFE6AEFAC";
private const string PORT_Name = "Example_SingleApplication";
private const string URL_Service = GUID_Application;
private const string URL_Client = "ipc://" + PORT_Name + "/" + GUID_Application;

static void Main(string[] args)
{
    // Feststellen, ob bereits eine Instanz gestartet wurde.
    bool createdNew;

    Mutex mutex = new Mutex(true, "EB943C9A-45ED-40AC-A562-94F3C0FFD730", out createdNew);
    if (createdNew)
    {
        // Als erste Instanz erzeuge ich einen neuen Kanal zur kommunikation für weitere Instanzen.
        ChannelServices.RegisterChannel(new IpcChannel(PORT_Name), false);

        RemotingConfiguration.ApplicationName = GUID_Application;
        RemotingConfiguration.RegisterWellKnownServiceType(
            typeof(InstanzArgumentsReceiver),
            URL_Service,
            WellKnownObjectMode.SingleCall);

        Console.WriteLine("Die Anwendung wurde gestartet.");
        Console.WriteLine("Ein Kommunikationskanal wurde erstellt.");
    }
    else
    {
        // Als weitere Instanz, senden wir die Parameter an die bereits gestartet Instanz und beenden uns wieder.
        Console.WriteLine("Die Anwendung läuft bereits.");

        if (args != null && args.Length > 0)
        {
            Console.WriteLine("Sende Parameter an die bereits laufende Instanz..");

            RemotingConfiguration.RegisterWellKnownClientType(
                typeof(InstanzArgumentsReceiver),
                URL_Client);

            InstanzArgumentsReceiver receiver = new InstanzArgumentsReceiver();
            receiver.SetArguments(args);
        }
    }

    Console.ReadKey(false);
}

Wie ihr sehen könnt, ist auch das weiterleiten von Parametern dank der bereits vor implementierten Technik (.NET Remoting) sehr schnell und einfach gemacht.

Ihr erstellt eine Klasse mit einer Methode, die die Parameter empfängt und leitet die Klasse von MarshalByRefObject ab.
Danach erzeugt ihr in der ersten Instanz einen Kanal und registriert den Dienst über RemotingConfiguration.RegisterWellKnownServiceType.
Nun können weitere Instanzen diesen Kanal ansprechen und Daten senden.

Die Beispiel-Projekte zum Download:
Nur eine Instanz zulassen
Parameter weitergeben

Ergänzungen und Feedback wie immer gewünscht. blink

Kommentare

  1. Wie frage ich die gesendeten Parameter in der ersten Instanz ab? Hier mein Code: http://pastebin.com/jD6335r3.
     
    In der ersten Instanz frage ich die Parameter ab mit: string[] arguments = Environment.GetCommandLineArgs();
     
    Das geht auch aber nur bei den Parametern die an die ersten Instanz von Windows gesendet wurden und nicht die Paramter der 2. Instanz.
  2. Hi King555,
     
    du erhälst über die Arguments (Environment.GetCommandLineArgs()) immer nur die Parameter der aktuellen Instanz. Der Trick ist, das über ein Mutex erkannt wird, dass bereits eine Instanz läuft. Danach werden die Parameter der neuen Instanz an die bereits gestartete Instanz gesendet.
     
    Starte bitte einfach mal das Beispielprojekt mehrfach und stell unter den Projekteinstellungen Debuggen / Befehlszeilenargumente ein paar Parameter ein. Dann wirst du sehen was ich meine.
     
    Die Zeile, welche die Parameter weiterleitet, befindet sich in Zeile 51:
    receiver.SetArguments(args);
  3. Wäre es möglich das du einen Beispiel Projekt erstellt in Windows Forms statt der Konsolen App?
Um einen Kommentar zu hinterlassen, ist eine Anmeldung erforderlich.