Hier ist eine sehr gute Artikel bezüglich der Mutex-Lösung. Der in dem Artikel beschriebene Ansatz ist aus zwei Gründen vorteilhaft.
Erstens erfordert es keine Abhängigkeit von der Microsoft.VisualBasic-Assembly. Wenn mein Projekt bereits eine Abhängigkeit von dieser Assembly hätte, würde ich wahrscheinlich die folgende Vorgehensweise empfehlen in einer anderen Antwort gezeigt . Aber wie es ist, verwende ich nicht die Microsoft.VisualBasic-Assembly, und ich würde lieber nicht eine unnötige Abhängigkeit zu meinem Projekt 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 Idee, die die anderen hier beschriebenen Mutex-Lösungen nicht bieten.
UPDATE
Mit Stand vom 1.8.2014 ist der oben verlinkte Artikel immer noch aktiv, aber der Blog wurde schon seit einiger Zeit nicht mehr aktualisiert. Das lässt mich befürchten, dass er irgendwann verschwinden könnte, und mit ihm die vorgeschlagene Lösung. Ich gebe den Inhalt des Artikels hier für die Nachwelt wieder. Der Text gehört ausschließlich dem Eigentümer des Blogs unter Sanity Free Coding .
Heute wollte ich einige Codes umstrukturieren, die meine Anwendung mehrere Instanzen von sich selbst auszuführen.
Zuvor hatte ich verwendet System.Diagnostik.Prozess für die Suche nach einem Instanz von myapp.exe in der Prozessliste zu suchen. Das funktioniert zwar, aber es bringt eine Menge Overhead mit sich, und ich wollte etwas Saubereres.
Da ich wusste, dass ich dafür eine Mutex verwenden konnte (aber es noch nie getan hatte) ), habe ich mich daran gemacht, meinen Code zu reduzieren und mein Leben zu vereinfachen.
In der Klasse meiner Anwendung main habe ich eine statische Klasse namens Mutex :
static class Program
{
static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
[STAThread]
...
}
Mit einem benannten Mutex können wir die Synchronisierung über mehrere mehrere Threads und Prozesse, was genau die Magie ist, die ich suche suche.
Mutex.WaitOne hat eine Überladung, die eine Zeitspanne angibt, die wir warten sollen. Da wir unseren Code nicht wirklich synchronisieren wollen synchronisieren wollen (sondern nur prüfen, ob er gerade in Gebrauch ist), verwenden wir die Überladung mit zwei Parametern: Mutex.WaitOne(Zeitspanne timeout, bool exitContext) . Wait one gibt true zurück, wenn er in der Lage ist, einzutreten, und false, wenn er nicht eingetreten ist. In diesem Fall wollen wir überhaupt nicht warten; wenn unsere Mutex gerade benutzt wird, überspringen wir ihn und machen weiter, also übergeben wir TimeSpan.Zero (warte 0 Millisekunden), und setzen den exitContext auf true, damit wir den Synchronisationskontext verlassen können, bevor wir versuchen, eine Sperre darauf zu erhalten. Mit verpacken wir unseren Application.Run-Code in etwas wie dieses:
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 Anwendung also läuft, gibt WaitOne false zurück, und wir erhalten eine Nachrichtenfeld.
Anstatt ein Meldungsfenster anzuzeigen, habe ich mich für ein kleines Win32-Tool entschieden um meine laufende Instanz zu benachrichtigen, dass jemand vergessen hat, dass sie bereits dass jemand vergessen hat, dass es bereits läuft (indem es sich an die Spitze aller anderen Fenster setzt). Um dies zu erreichen, habe ich PostMessage um eine benutzerdefinierte Nachricht an alle Fenster zu senden (die benutzerdefinierte Nachricht wurde mit RegisterWindowMessage meiner laufenden Anwendung, was bedeutet, dass nur meine Anwendung weiß, was ist), dann wird meine zweite Instanz beendet. Die laufende Anwendungsinstanz erhält diese Benachrichtigung und verarbeitet sie. Um das zu tun, habe ich außer Kraft gesetzt. WndProc in meinem Hauptformular und hörte für meine benutzerdefinierte Benachrichtigung. Als ich diese Benachrichtigung erhielt, setzte ich die TopMost-Eigenschaft des Formulars auf true gesetzt, um es ganz nach oben zu bringen.
Ich habe folgendes Ergebnis erzielt:
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);
}
}
}
// 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;
}
}
17 Stimmen
Gibt die CLR nicht automatisch alle nicht freigegebenen Mutexe frei, wenn die Anwendung ohnehin beendet wird?
2 Stimmen
@Cocowalla: Der Finalisierer sollte die nicht verwalteten Mutexe entsorgen, es sei denn, er kann nicht wissen, ob die Mutex von der verwalteten Anwendung erstellt oder an eine bestehende angehängt wurde.
1 Stimmen
Es ist sinnvoll, nur eine Instanz Ihrer Anwendung zu haben. Aber die Übergabe von Argumenten an eine bereits existierende Anwendung erscheint mir ein wenig albern. Ich kann keinen Grund dafür sehen, dies zu tun. Wenn Sie eine Anwendung mit einer Dateierweiterung verknüpfen, sollten Sie so viele Anwendungen öffnen, wie Benutzer Dokumente öffnen wollen. Das ist das Standardverhalten, das jeder Benutzer erwarten würde.
1 Stimmen
Ich möchte nur eine Korrektur meiner vorherigen Aussage vornehmen. Die Übergabe von Argumenten an eine bestehende Anwendung bedeutet, dass Sie eine MDI (Multi-Document-Interface) durchführen wollen. Ich dachte, dass MDI war eine Art, die Microsoft war drängen aus (Word und Excel sind jetzt SDI). Aber ich habe festgestellt, dass Chrome und IE beide MDI sind. Vielleicht sind wir in Jahren, in denen MDI zurück ist? (Aber ich bevorzuge immer noch SDI gegenüber MDI)
13 Stimmen
@Cocowalla Die CLR verwaltet keine nativen Ressourcen. Wenn ein Prozess jedoch beendet wird, werden alle Handles vom System (dem Betriebssystem, nicht der CLR) freigegeben.
2 Stimmen
Ich bevorzuge die Antwort von @huseyint. Sie verwendet die Microsoft-eigene Klasse 'SingleInstance.cs', so dass man sich keine Gedanken über Mutexe und IntPtrs machen muss. Außerdem besteht keine Abhängigkeit von VisualBasic (igitt). Siehe codereview.stackexchange.com/questions/20871/ für mehr...
1 Stimmen
Ich verwende SingleInstanceApp nuget. nuget.org/packages/SingleInstanceApp Funktioniert perfekt. Benötigt keine Microsoft.VisualBasic-Referenz. Hängt nicht von der App-Version ab (in Microsoft.VisualBasic schon). Die App wird nur durch eine eindeutige Zeichenfolge identifiziert.
0 Stimmen
Es sieht so aus, als ob diese Frage und die Antworten eine Fülle von Informationen enthalten - eine wahre Goldgrube!