Laden...

mehrere Programminstanzen verhindern?

Erstellt von Jaden vor 19 Jahren Letzter Beitrag vor 13 Jahren 78.241 Views
Information von herbivore vor 17 Jahren

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!

J
Jaden Themenstarter:in
21 Beiträge seit 2004
vor 19 Jahren
mehrere Programminstanzen verhindern?

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

D
279 Beiträge seit 2004
vor 19 Jahren

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.

Warnung von herbivore vor 14 Jahren

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.

J
Jaden Themenstarter:in
21 Beiträge seit 2004
vor 19 Jahren

Danke für die schnelle Antwort!

Hat super funktioniert 🙂

Schöne Grüße

Jaden

S
201 Beiträge seit 2005
vor 18 Jahren

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

S
709 Beiträge seit 2005
vor 18 Jahren
Statische Variable

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

S
201 Beiträge seit 2005
vor 18 Jahren

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.
S
709 Beiträge seit 2005
vor 18 Jahren

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

Gruß,
SimonKnight6600

S
709 Beiträge seit 2005
vor 18 Jahren

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

S
709 Beiträge seit 2005
vor 18 Jahren

Ich glaube ich hab was gefunden: A Single Instance Application which Minimizes to the System Tray when Closed

This article addresses three issues:

  1. Creating a single instance application.
    2. Restoring the previous instance, if user tries to launch another instance.
  2. 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.

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo zusammen,

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

herbivore

S
709 Beiträge seit 2005
vor 18 Jahren

Hallo herbivore!

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

Danke,
SimonKnight6600

N
177 Beiträge seit 2006
vor 17 Jahren

Ich glaube dieser Thread verdient ein Update:
Single-Instance C# Application - for .NET 2.0

R
9 Beiträge seit 2006
vor 17 Jahren

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

G
40 Beiträge seit 2005
vor 17 Jahren

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

49.485 Beiträge seit 2005
vor 17 Jahren

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

3.971 Beiträge seit 2006
vor 15 Jahren

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.

Warnung von herbivore vor 14 Jahren

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.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

F
2 Beiträge seit 2009
vor 15 Jahren

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"));
            }
        }
P
219 Beiträge seit 2008
vor 14 Jahren

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 😃

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo zusammen,

sollte es mit dem Mutex-Vorschlag Probleme im Release-Modus geben, hier ein Tipp von Th69:

Lösung also laut Link
>
:

  • die Mutex-Variable als statischen Member definieren
  • GC.KeepAlive(mutex)

herbivore

C
252 Beiträge seit 2007
vor 13 Jahren

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