Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
mehrere Programminstanzen verhindern?

Moderationshinweis von herbivore (26.04.2006 - 08:30)

Dies ist ein Thread, auf den aus der FAQ verwiesen wird. Bitte keine weitere Diskussion, sondern nur wichtige Ergänzungen und diese bitte knapp und präzise. Vielen Dank!

Jaden
myCSharp.de - Member



Dabei seit:
Beiträge: 21

Themenstarter:

mehrere Programminstanzen verhindern?

beantworten | zitieren | melden

Hi,

kann mir vielleicht jemand helfen, ich hab ne Datenanwendung geschrieben nur das Problem ist, wenn ich mehere Programminstanzen gestartet habe, kann es ja zu Datenbankkorruption führen. Also wenn ich die exe mehrfach ausführe, startet sich das Programm auch mehrfach.

Gibt es eine Einstellung, wo ich das ändern kann, also wenn das Programm schon gestartet ist, soll es sich nicht nochmal starten lassen können.

Wäre dankbar, wenn mir jemand helfen würde!

Vielen Dank im Voraus!

Schöne Grüße

Jaden
private Nachricht | Beiträge des Benutzers
Denny
myCSharp.de - Member



Dabei seit:
Beiträge: 279
Herkunft: Koblenz

beantworten | zitieren | melden

Hallo,

am besten machst du das per Mutex. Schau mal in How do I make sure that only one instance of my application runs at a time? und SingleInstance Component (Winform).

Hier mal ein Beispielcode:



// This will be set to true if this process can own the mutex. 
bool pobjIOwnMutex = false; 

// Try and get ownership of a mutex who's name is known. 
System.Threading.Mutex pobjMutex = new System.Threading.Mutex(true, "1a04779c-60ab-40cc-bd19-28f72fbeb2bf", out pobjIOwnMutex); 

 // If the mutex is owned by this process, then run the application. 
if (pobjIOwnMutex) 
{ 
	// Run the application. 
	Application.Run(new frmMain()); 
} 
else 
{ 
	//exit application 
} 

Das ganze gehört natürlich in die Main-Methode.

Moderationshinweis von herbivore (20.07.2009 - 13:46)

Achtung: Die GUID 1a04779c-60ab-40cc-bd19-28f72fbeb2bf ist nur ein Beispiel und muss bei der Übernahme des Codes unbedingt durch eine eigene GUID oder einen anderen weltweit eindeutigen String ersetzt werden.

Achtung: Man muss dafür sorgen, dass der GC den Mutex nicht aufräumt, bevor das Programm beendet ist.

private Nachricht | Beiträge des Benutzers
Jaden
myCSharp.de - Member



Dabei seit:
Beiträge: 21

Themenstarter:

beantworten | zitieren | melden

Danke für die schnelle Antwort!

Hat super funktioniert

Schöne Grüße

Jaden
private Nachricht | Beiträge des Benutzers
stift
myCSharp.de - Member



Dabei seit:
Beiträge: 201

beantworten | zitieren | melden

Hallo,

ich haette zu diesen Thema eine erweiterte Frage, wenn das Programm bereits laeuft kann wenn es dann in den Vordergrund holen bzw. ein Form oeffnen?

MFG
private Nachricht | Beiträge des Benutzers
SimonKnight6600
myCSharp.de - Member



Dabei seit:
Beiträge: 709
Herkunft: Österreich

Statische Variable

beantworten | zitieren | melden

Mhm... Vieleicht würde es gehen das gewünschte Fenster als statische Variable zu deklarieren und bei dieser dann die BringToFront() Methode aufzurufen.


Gruß,
SimonKnight6600
private Nachricht | Beiträge des Benutzers
stift
myCSharp.de - Member



Dabei seit:
Beiträge: 201

beantworten | zitieren | melden

Das Form ist Statisch, aber das Problem ist glaube ich:

1. Aufruf von test.exe
2. Aufruf von test.exe -> test.exe laeuft -> Form aus 1. Aufruf von test.exe oeffnen -> 2. Aufruf von test.exe schliessen.
private Nachricht | Beiträge des Benutzers
SimonKnight6600
myCSharp.de - Member



Dabei seit:
Beiträge: 709
Herkunft: Österreich

beantworten | zitieren | melden

Achso, hab da nen kleinen Denkfehler gehabt Da fällt mir jetzt leider gerade auch nichts ein...

Gruß,
SimonKnight6600
private Nachricht | Beiträge des Benutzers
SimonKnight6600
myCSharp.de - Member



Dabei seit:
Beiträge: 709
Herkunft: Österreich

beantworten | zitieren | melden

Da gabs ja mal den Thread Process mit Kommandos steuern. Wenn das ginge, könntest du dem ersten Aufruf ein Kommando schicken und das ganze wäre gebacken. Aber bei dem Thread ist leider nicht rausgekommen wie man sowas in C# implementiert. Schau mal auf Codeproject, vieleicht findest du da was.

Gruß,
SimonKnight6600
private Nachricht | Beiträge des Benutzers
SimonKnight6600
myCSharp.de - Member



Dabei seit:
Beiträge: 709
Herkunft: Österreich

beantworten | zitieren | melden

Ich glaube ich hab was gefunden: A Single Instance Application which Minimizes to the System Tray when Closed
Zitat
This article addresses three issues:

1. Creating a single instance application.
2. Restoring the previous instance, if user tries to launch another instance.
3. Minimizing (with animation) the application to the Notification Area of the task bar when the window is closed.

Ich glaube, das müsste es sein wenn mich meine Englischkenntnisse nicht täuschen.

Gruß,
SimonKnight6600

//edit: Ja, dort stehts. Du musst halt WinAPI einsetzen.
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 49.486
Herkunft: Berlin

beantworten | zitieren | melden

Hallo zusammen,

es geht auch mit .NET-Mitteln. Stichwort: Remoting, siehe das Beispiel in Dateiassoziation.

herbivore
private Nachricht | Beiträge des Benutzers
SimonKnight6600
myCSharp.de - Member



Dabei seit:
Beiträge: 709
Herkunft: Österreich

beantworten | zitieren | melden

Hallo herbivore!

Danke für den Link! Hab den Code gerade getestet. Nach sowas suche ich schon ne Weile.

Danke,
SimonKnight6600
private Nachricht | Beiträge des Benutzers
nop
myCSharp.de - Member



Dabei seit:
Beiträge: 177

beantworten | zitieren | melden

Ich glaube dieser Thread verdient ein Update:
Single-Instance C# Application - for .NET 2.0
private Nachricht | Beiträge des Benutzers
rifmetroid
myCSharp.de - Member



Dabei seit:
Beiträge: 9

beantworten | zitieren | melden

Hier ein einfacher Codeschnipsel für .NET 2.0 um mehrere Programminstanzen zu verhindern. Dafür muss man die Microsoft.VisualBasic als Verweis im Projekt hinzufügen.



using Microsoft.VisualBasic.ApplicationServices;

namespace Test
{
public class SingleInstanceApplication : WindowsFormsApplicationBase
    {
        private SingleInstanceApplication()
        { 
            base.IsSingleInstance = true; 
        }

        public static void Run(Form f, StartupNextInstanceEventHandler startupHandler)
        {
            SingleInstanceApplication app = new SingleInstanceApplication();
            app.MainForm = f;
            app.StartupNextInstance += startupHandler;
            app.Run(Environment.GetCommandLineArgs());
        }
    }

    static class Program
    {
        /// <summary>
        /// Der Haupteinstiegspunkt für die Anwendung.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            SingleInstanceApplication.Run(new Form1(), StartupNextInstanceHandler);         
        }

        static void StartupNextInstanceHandler(object sender, StartupNextInstanceEventArgs e)
        {
            // do whatever you want here with e.CommandLine...
        }
    }
}

MfG

Rifmetroid
private Nachricht | Beiträge des Benutzers
gespannter_Bogen
myCSharp.de - Member



Dabei seit:
Beiträge: 40

beantworten | zitieren | melden

Mehrere Programminstanzen verhindern. Was spricht dagegen, es so zu machen:



using System.Diagnostics;

// in Main, am Anfang:

Process dieseAnwendung = Process.GetCurrentProcess();

Process[] jetztlaufendeProzesse = Process.GetProcessesByName(dieseAnwendung.ProcessName);

if (jetztlaufendeProzesse.Length > 1)
      {
        System.Windows.Forms.MessageBox.Show("C#-Anwendung nicht mehrfach laufen lassen\nbitte erst das letzt benutzte Druckfenster schliessen. Siehe auch in der Taskleiste unten");
        return;
      }
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 49.486
Herkunft: Berlin

beantworten | zitieren | melden

Hallo gespannter_Bogen,

z.B. das verschiedene Programme zufällig den gleichen EXE-Namen haben können. Das Semphorenbeispiel von oben braucht außerdem auch nicht mehr Code als deins und ist die professionelle Variante. Davon abgesehen wird auch deine Variante vermutlich in den allermeisten Fällen funktionieren.

herbivore
private Nachricht | Beiträge des Benutzers
kleines_eichhoernchen
myCSharp.de - Member

Avatar #avatar-2079.jpg


Dabei seit:
Beiträge: 3.971
Herkunft: Ursprünglich Vogtland, jetzt Much

beantworten | zitieren | melden

Um Fehlverhalten des Mutexes auszuschließen, muss man den Mutex bis zum Programmende am Leben erhalten, so dass dieser nicht vom GC eingesammelt werden kann. Wird der Mutex vom GC eingesammelt und anschließend ein neue Programminstanz erstellt, wird automatisch die neue Programminstanz zum neuen Besitzer des Mutex, die andere Anwendung läuft aber eventuell noch weiter. So hat man anschließend (un)gewollt 2 Instanzen laufen.

Das Beispiel von Danni müsste anschließend so aussehen:


public shared void Main() {
  // This will be set to true if this process can own the mutex. 
  bool pobjIOwnMutex = false;

  // Try and get ownership of a mutex who's name is known. 
  System.Threading.Mutex pobjMutex = new System.Threading.Mutex(true, "1a04779c-60ab-40cc-bd19-28f72fbeb2bf", out pobjIOwnMutex);

  // If the mutex is owned by this process, then run the application. 
  if (pobjIOwnMutex)
  {
     try {
       // Run the application. 
       Application.Run(new frmMain());
     }
     finally {
       pobjMutex.Close();
     }
  }
  else
  {
    //exit application 
  }
}
Es ist aber auch möglich eine statische Variable zur Speicherung des Mutex zu benutzen.

Was Hingegen aber nicht ausreicht, ist die Mutex-Variable einfach auf null zu setzen und die Variable später nicht mehr zu verwenden. Der Compiler erkennt eine solche sinnlos-Anweisung und rationalisiert diese weg.
Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

Moderationshinweis von herbivore (20.07.2009 - 13:46)

Die GUID 1a04779c-60ab-40cc-bd19-28f72fbeb2bf ist nur ein Beispiel und muss bei der Übernahme des Codes unbedingt durch eine eigene GUID ersetzt werden.

private Nachricht | Beiträge des Benutzers
fridolin
myCSharp.de - Member



Dabei seit:
Beiträge: 2

beantworten | zitieren | melden

hi

das ganze funtioniert auch nur, solange das programm nicht aus einem anderen benutzerkonto heraus gestartet wurde.

ich hatte das problem, wenn ich auf meinem konto das programm gestartet hatte, mein benutzerkonto nicht abgemeldet wird, sondern mit "benutzer wechseln" ein anderes konto aktiviert wird.
startet man nun das programm aus diesem konto heraus, hängt sich die ganze geschichte auf.
seit vista und UAC funtioniert das mit dem getProcess auch nimmer, da der process mit "runas" neu gestartet wird und bei getprocess auf jeden fall ein true bekommt.

ich habe das so gelöst, daß ich bei start eine temporäre datei erzeuge, in der die domain und der benutzername steht.
das funktioniert prima:

in der mainClass:

using System;
using System.Diagnostics;
using System.Reflection;
using System.Security.Principal;
using System.Windows.Forms;
using SpecialServices;

namespace oxylbox
{
    /// <summary>
    /// Summary description for MainClass.
    /// </summary>
    public class MainClass
    {
        // Der Haupteinstiegspunkt für den Vorgang
        [STAThread]
        public static void Main()
        {
            using (SingleProgramInstance spi = new SingleProgramInstance("x5k6yz"))
            {
                if (spi.IsSingleInstance)
                {
                    Application.EnableVisualStyles();
                    string currentUser = UserClass.Read_CurrentUser();

                    if (currentUser != null && currentUser != "" 
                        && CheckIfAProcessIsRunning("oxyl~box"))
                    {
                        MessageBox.Show("oxyl~box wurde bereits von dem Benutzerkonto \"" + currentUser + "\" gestartet!\r\n" +
                                "Bitte wechseln Sie zu dem Benutzerkonto \"" + currentUser + "\" und beenden Sie oxyl~box.",
                                "oxyl~box MultiStart", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                        Application.Exit();
                        return;
                    }

                    // Betriebsystem prüfen: XP oder höher (XP = 5, Vista = 6)
                    if (System.Environment.OSVersion.Version.Major ≥ 5)
                    {
                        if (!IsElevated())
                        {
                            ProcessStartInfo startInfo = new ProcessStartInfo();
                            startInfo.UseShellExecute = true;
                            startInfo.WorkingDirectory = Environment.CurrentDirectory;
                            startInfo.FileName = Assembly.GetExecutingAssembly().Location;
                            startInfo.Verb = "runas";

                            try
                            {
                                Process p = Process.Start(startInfo);
                                Application.Exit();
                                return;
                            }
                            catch (System.ComponentModel.Win32Exception ex)
                            {
                                // benutzer hat abgebrochen
                                MessageBox.Show("oxyl~box wird nicht gestartet.\r\n"  +  ex.Message, "oxyl~box");
                                Application.Exit();
                                return;
                            }
                        }
                    }
                    else
                    {
                        MessageBox.Show("oxyl~box benötigt mindestens Win-XP oder höher!", "Betriebssystem");
                        Application.Exit();
                        return;
                    } 
                    
                    Application.SetCompatibleTextRenderingDefault(true);
                    Application.Run(new Form1());
                }
                else
                {
                    spi.RaiseOtherProcess();
                }
            }
        }

        static internal bool IsElevated()
        {
            WindowsIdentity id = WindowsIdentity.GetCurrent();
            WindowsPrincipal p = new WindowsPrincipal(id);
            return p.IsInRole(WindowsBuiltInRole.Administrator);
        }

        private static bool CheckIfAProcessIsRunning(string processname)
        {
            return Process.GetProcessesByName(processname).Length > 0;
        }
    }
}

dann eine UserClass erstellen:

using System;
using System.IO;
using System.Management;
using System.Windows.Forms;

namespace oxylbox
{
    class UserClass
    {
        private static StreamWriter sw;
        private static StreamReader sr;

        public static string getUserName()
        {
            //Code given and modified with permission and courtesy of Ruslan Dzanmahmudov
            // Declare private variables.
            string nSearch = string.Format(@"SELECT * FROM Win32_ComputerSystem");
            ManagementObjectSearcher nSearcher = new ManagementObjectSearcher();

            // Set the properties.
            nSearcher.Query = new ObjectQuery(nSearch);
            ManagementObjectCollection nWMI = nSearcher.Get();

            // Parsing through all objects.
            foreach (ManagementObject _oManagement in nWMI)
            {
                // Return the current clock speed.
                return _oManagement["UserName"].ToString();
            }

            // Cannot get any information.
            return string.Empty;
        }

        public static void Write_CurrentUser(string path)
        {
            try
            {
                sw = new StreamWriter(path, false);
                sw.WriteLine(getUserName());
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "oxyl~box Beutzeridentifikation",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                try
                {
                    sw.Flush();
                    sw.Close();
                    sw.Dispose();
                }
                catch { }
            }
        }

        public static string Read_CurrentUser()
        {
            string path = Path.Combine(Application.StartupPath, "InUse.tmp");

            if (File.Exists(path))
            {
                try
                {
                    sr = new StreamReader(path, false);
                    string currentUser = sr.ReadLine();
                    return currentUser;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "oxyl~box Beutzeridentifikation",
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                finally
                {
                    try
                    {
                        sr.Close();
                        sr.Dispose();
                    }
                    catch { }
                    path = null;
                }
            }

            return null;
        }
    }
}

und in der Form1(bzw: Mainform):

private void Form1_Load(object sender, EventArgs e)
        {
            UserClass.Write_CurrentUser(Path.Combine(Application.StartupPath, "InUse.tmp"));
        }

private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (File.Exists(Path.Combine(Application.StartupPath, "InUse.tmp")))
            {
                File.Delete(Path.Combine(Application.StartupPath, "InUse.tmp"));
            }
        }
private Nachricht | Beiträge des Benutzers
PoWl
myCSharp.de - Member



Dabei seit:
Beiträge: 219

beantworten | zitieren | melden

Wenn ich nun auch mal meinen Senf dazugeben darf. Für alle die es kurz und knackig haben wollen erstellen ein neues WindowsForms Projekt, fügen bei den Verweisen "Microsoft.VisualBasic" hinzu, öffnen die Program.cs und schreiben dort folgendes rein:

using System;
using System.Windows.Forms;
using Microsoft.VisualBasic.ApplicationServices;


namespace OnyOne
{
  class Program : WindowsFormsApplicationBase
  {
    public Program()
    {
      this.IsSingleInstance = true;
      this.EnableVisualStyles = true;
    }

    protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
    {
      base.OnStartupNextInstance(eventArgs);
      MessageBox.Show("Diese Anwendung lässt sich nur einmal starten!","Fehler!");
      eventArgs.BringToForeground = true;
    }

    [STAThread]
    static void Main(string[] args)
    {
      Program MyApp = new Program();
      MyApp.ApplicationContext.MainForm = new Form1();
      MyApp.Run(args);
    }
  }
}

Natürlich noch den Namespace anpassen. Getestet unter WinXP :-)
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 49.486
Herkunft: Berlin

beantworten | zitieren | melden

Hallo zusammen,

sollte es mit dem Mutex-Vorschlag Probleme im Release-Modus geben, hier ein Tipp von Th69:
Zitat von Th69
Lösung also laut Link Mutex reagiert nur im debugmodus :
- die Mutex-Variable als statischen Member definieren
- GC.KeepAlive(mutex)

herbivore
private Nachricht | Beiträge des Benutzers
chavez
myCSharp.de - Member



Dabei seit:
Beiträge: 252
Herkunft: Österreich

beantworten | zitieren | melden

Ich hatte das Mutex-Problem auch schon. Seitedem pack ich den Mutex immer in ein using und das Problem ist erledigt.

Hier der Code wie ich es früher immer verwendet habe. Natürlich ohne GC.Collect(). Das habe ich nur eingebaut um das Problem auf Anhieb zu reproduzieren und damit der Mutex entsorgt wird.


      bool firstInstance;
      Mutex m = new Mutex(true, "nameMutex", out firstInstance);  // Mutex wird (möglicherweise) vorzeitig zerstört
      if (firstInstance)
      {
        GC.Collect(); // um das Problem auf Anhieb zu reproduzieren
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
      }
      else
        MessageBox.Show("already running!");

Hier nun der Code den ich seit 2,5 Jahren verwende und bisher immer funktioniert hat. Jetzt wird trotz GC.Collect() der Mutex nicht entsorgt.


      bool firstInstance;
      using (Mutex m = new Mutex(true, "nameMutex", out firstInstance))  // Mutex lebt bis zum Programmende
      {
        if (firstInstance)
        {
          GC.Collect();
          Application.EnableVisualStyles();
          Application.SetCompatibleTextRenderingDefault(false);
          Application.Run(new Form1());
        }
        else
          MessageBox.Show("already running!");
      }
private Nachricht | Beiträge des Benutzers