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
Programm on Top

Moderationshinweis von herbivore (26.04.2006 - 08:31)

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!

stift
myCSharp.de - Member



Dabei seit:
Beiträge: 201

Themenstarter:

Programm on Top

beantworten | zitieren | melden

Hallo,

ich moechte gerne, wenn ein Programm erneut aufgerufen wird, dass es nicht erneut startet, sondern, dass das Form der bereits laufenden Instanz on Top angezeigt wird.

Hat jemand einen Loesung?
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

WICHTIG: Um ein Fenster eines anderen Prozesses zuverlässig(!) so in den Vordergrund zu bringen, dass es gleichzeitig aktiv wird und dadurch den Fokus bekommt, sind vier(!) Schritte erforderlich, bei denen die beiden Prozesse zudem abgestimmt und koordiniert vorgehen müssen. Eine genaue Beschreibung/Erklärung dazu sowie eine konkrete, in Code gegossene Lösung findet sich in diesem Beitrag weiter unten (am momentanen Ende dieses Threads).

ACHTUNG: Die anderen Lösungsvorschläge in diesem und anderen Threads - meine eigenen eingeschlossen - sind entweder für andere Situationen gedacht oder unvollständig oder sogar gänzlich ungeeignet.



Hallo stift,

siehe Andere Instanz sichtbar machen

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



Dabei seit:
Beiträge: 201

Themenstarter:

beantworten | zitieren | melden

Ist es nicht besser es ueber semaphore dies zu relaisieren.
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 stift,

was meinst du damit? Es wird doch eine Semaphore verwendet, um zu erkennen, ob schon eine Instanz läuft. Für die Kommunikation mit der bestehenden Instanz wird dann Remoting benutzt.

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



Dabei seit:
Beiträge: 201

Themenstarter:

beantworten | zitieren | melden

Fuer die Kommunikation auch Semaphore verwenden, oder Mutex.
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 stift,

axo, ja kann man in diesem Fall machen, weil ja keine Daten übertragen werden müssen.

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



Dabei seit:
Beiträge: 201

Themenstarter:

beantworten | zitieren | melden

Und wie?
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 stift,

die erste Instanz muss einen Thread erstellen, der in einer "Endlos"-Schleife abwechselnd auf eine weitere Benannte Semaphore wartet und BringToFront aufruft (natürlich per Control.BeginInvoke).

Eine weitere Instanz muss dann nur der Semaphore signalisieren.

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



Dabei seit:
Beiträge: 201

Themenstarter:

beantworten | zitieren | melden

Die Endlosschleife kostet doch "sehr" viel Performence?
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 stift,

im Allgemeinen vielleicht, diese aber nicht, weil der Thread ja beim Warten auf die Semaphore "schläft". Die Schleife wird also nur einmal pro neuer Instanz durchlaufen.

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



Dabei seit:
Beiträge: 201

Themenstarter:

beantworten | zitieren | melden

Die schleife muss doch immer durchlaufen in der 1.Instanz, dass diese mit bekommt, dass eine weitere Instanz geoeffnet worden ist, und dann diese(1.Instanz) die Action(BringToFront()) ausfuehrt.
Oder?
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 stift,

nein, es ist so wie ich sagte.

Die erste Instanz bekommt durch das Signalisieren der Semaphore mit, wann und das was zu tun ist, führt dann genau einmal BringToFront aus und wartet (schlafend) erneut auf die Semaphore. Die CPU-Last dieser Schleife ist 0%.

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



Dabei seit:
Beiträge: 8.746
Herkunft: Berlin

beantworten | zitieren | melden

Stift: Synchronisationsobjekte haben die Eigenschaft, dass man auf sie "warten" kann. In diesem Fall bedeutet das, dass die Schleife nicht dauern läuft, sondern eigentlich immer schläft, also in der Warteoperation hängt.

Muss ein Thread auf ein Sync-Objekt warten, dann erkennt dies der Scheduler und bringt den Thread nicht zur Ausführung. Ein wartenden Thread kostet also wirklich Null Rechenzeit, nichtmal für den Kontext-Switch.

Aus diesem Grund kann man Threads auch nicht "abschiessen", zumindest solange sie hängen. Sie können nicht auf eine Abschuss-Botschaft reagieren weil sie nicht drankommen.
private Nachricht | Beiträge des Benutzers
stift
myCSharp.de - Member



Dabei seit:
Beiträge: 201

Themenstarter:

beantworten | zitieren | melden

@herbivore: Der Code darfuer duerfte nicht aufwendig sein, oder? Hast du den Code?e
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 stift,

der Code dürfe in der Tat nicht aufwändig sein. Nette Übungsaufgabe für dich. :-)

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



Dabei seit:
Beiträge: 201

Themenstarter:

beantworten | zitieren | melden

Ok, ich versuche es. Aber eine kleine Anfangshilfe koenntest du mir schon geben.
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 stift,

steht doch oben alles schon. Mehr ist das nicht.

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



Dabei seit:
Beiträge: 201

Themenstarter:

beantworten | zitieren | melden

OK, ich probiere es morgen einmal.
Bis Morgen.
private Nachricht | Beiträge des Benutzers
stift
myCSharp.de - Member



Dabei seit:
Beiträge: 201

Themenstarter:

beantworten | zitieren | melden

Ich denke irgendwie immer noch, dass wir uns irgendwie falsch verstehen.

Ich habe eine exe die ich aufrufe(1.Instanz), dann rufe ich die gleiche exe nocheinmal auf (2. Instanz) .

Und dann soll eine Funktion bei der 1. Instanz aufgerufen werden.
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 stift,

so habe ich es die ganze Zeit verstanden und gemeint.

Bei dem Remotingbeispiel ist das auch genau so. Da wird wirklich die Funktion (remote) aufgerufen.

In dem Beispiel mit den Semaphoren ist das nicht wirklich ein entfenter Aufruf, aber es läuft im Effekt auf das gleiche hinaus.

Und es ist wirklich nicht schwer. Die erste Instanz startet einen Thread. Der Thread erzeugt die Semaphore. Dann geht er in eine "Endlosschleife", in der er auf die Semaphore wartet. Die zweite Anweisung in der Schleife ist ein Invoke einer Methode, die BringToFront aufruft.

Die zweite Instanz signalisiert der Semaphore.

Das einzige was jetzt noch fehlt, ist dass die erste Instanz den Thread beenden kann, wenn sie selbst beendet werden soll und ein bisschen Fehlerbehandlung. Aber das kann man auch in einem zweiten Schritt angehen.

Eine Semaphore ist eine Ampel mit den drei Funktionen "Ampel auf rot setzen", Ampel auf grün setzen" und vor "Ampel auf grün warten". Der Thread setzt die Ampel auf rot und wartet dann auf grün. Die zweite Instanz setzt auf grün. Man sollte eine Semaphore verwenden, die alleine wieder auf rot springt.

Das war jetzt mal etwas ausführlicher, aber im Prinzip habe ich das schon die ganze Zeit so gesagt.

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



Dabei seit:
Beiträge: 201

Themenstarter:

beantworten | zitieren | melden

Und wie lautet die FUnktion "ampel auf gruen warten";

Mein Source Code:


using System;
using System.Threading;
using System.Security.AccessControl;

internal class Example
{
    internal static void Main()
    {
        const string semaphoreName = "SemaphoreExample5";

        Semaphore sem = null;
        bool doesNotExist = false;
        bool unauthorized = false;

        // Attempt to open the named semaphore.
        try
        {
            // Open the semaphore with (SemaphoreRights.Synchronize
            // | SemaphoreRights.Modify), to enter and release the
            // named semaphore.
            //
            sem = Semaphore.OpenExisting(semaphoreName);
        }
        catch(WaitHandleCannotBeOpenedException)
        {
            Console.WriteLine("Semaphore does not exist.");
            doesNotExist = true;
        }
        catch(UnauthorizedAccessException ex)
        {
            Console.WriteLine("Unauthorized access: {0}", ex.Message);
            unauthorized = true;
        }

        // There are three cases: (1) The semaphore does not exist.
        // (2) The semaphore exists, but the current user doesn't 
        // have access. (3) The semaphore exists and the user has
        // access.
        //
        if (doesNotExist)
        {
            // The semaphore does not exist, so create it.
            //
            // The value of this variable is set by the semaphore
            // constructor. It is true if the named system semaphore was
            // created, and false if the named semaphore already existed.
            //
            bool semaphoreWasCreated;

            // Create an access control list (ACL) that denies the
            // current user the right to enter or release the 
            // semaphore, but allows the right to read and change
            // security information for the semaphore.
            //
            string user = Environment.UserDomainName + "\\" 
                + Environment.UserName;
            SemaphoreSecurity semSec = new SemaphoreSecurity();

            SemaphoreAccessRule rule = new SemaphoreAccessRule(
                user, 
                SemaphoreRights.Synchronize | SemaphoreRights.Modify, 
                AccessControlType.Allow);
            semSec.AddAccessRule(rule);
            /*
            rule = new SemaphoreAccessRule(
                user, 
                SemaphoreRights.ReadPermissions | SemaphoreRights.ChangePermissions,
                AccessControlType.Allow);
            */
            rule = new SemaphoreAccessRule(user,
                 SemaphoreRights.Synchronize | SemaphoreRights.Modify,
                 AccessControlType.Allow);

            semSec.AddAccessRule(rule);

            // Create a Semaphore object that represents the system
            // semaphore named by the constant 'semaphoreName', with
            // maximum count three, initial count three, and the
            // specified security access. The Boolean value that 
            // indicates creation of the underlying system object is
            // placed in semaphoreWasCreated.
            //
            sem = new Semaphore(3, 3, semaphoreName, 
                out semaphoreWasCreated, semSec);

            // If the named system semaphore was created, it can be
            // used by the current instance of this program, even 
            // though the current user is denied access. The current
            // program enters the semaphore. Otherwise, exit the
            // program.
            //

            if (semaphoreWasCreated)
            {
                Console.WriteLine("Created the semaphore.");
            }
            else
            {
                Console.WriteLine("Unable to create the semaphore.");
                return;
            }

        }
        else if (unauthorized)
        {
            // Open the semaphore to read and change the access
            // control security. The access control security defined
            // above allows the current user to do this.
            //
            try
            {
                /*
                sem = Semaphore.OpenExisting(
                    semaphoreName,
                    SemaphoreRights.ReadPermissions
                        | SemaphoreRights.ChangePermissions);

                // Get the current ACL. This requires 
                // SemaphoreRights.ReadPermissions.
                SemaphoreSecurity semSec = sem.GetAccessControl();

                string user = Environment.UserDomainName + "\\"
                    + Environment.UserName;


                // First, the rule that denied the current user 
                // the right to enter and release the semaphore must
                // be removed.
                SemaphoreAccessRule rule = new SemaphoreAccessRule(
                    user,
                    SemaphoreRights.Synchronize | SemaphoreRights.Modify,
                    AccessControlType.Deny);
                semSec.RemoveAccessRule(rule);

                // Now grant the user the correct rights.
                // 

                rule = new SemaphoreAccessRule(user,
                     SemaphoreRights.Synchronize | SemaphoreRights.Modify,
                     AccessControlType.Allow);

                semSec.AddAccessRule(rule);

                // Update the ACL. This requires
                // SemaphoreRights.ChangePermissions.
                sem.SetAccessControl(semSec);
                */
                Console.WriteLine("Updated semaphore security.");

                // Open the semaphore with (SemaphoreRights.Synchronize 
                // | SemaphoreRights.Modify), the rights required to
                // enter and release the semaphore.
                //
                //sem = Semaphore.OpenExisting(semaphoreName);

            }
            catch (UnauthorizedAccessException ex)
            {
                Console.WriteLine("Unable to change permissions: {0}", ex.Message);
                return;
            }
        }
        else
        {
            Console.WriteLine("<-Close()->");
        }
    

        // Enter the semaphore, and hold it until the program
        // exits.
        //
        try
        {
            sem.WaitOne();
            Console.WriteLine("Entered the semaphore.");            
            Console.WriteLine("Press the Enter key to exit.");
            Console.ReadLine();
            sem.Close();
        }
        catch(UnauthorizedAccessException ex)
        {
            Console.WriteLine("Unauthorized access: {0}", ex.Message);
        }
    }
}
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 stift,

[EDIT=2017]Weiter unten findet sich Code, in dem zusätzlich auch das in den Vordergrund bringen implementiert ist.[/EDIT]

der Code sieht kompliziert aus, komplizierter als es sein müsste. Deshalb habe ich ihn mir nicht angeguckt. :-) Mit Semaphore habe ich die ganze Zeit nicht die Klasse gemeint, sondern der informatischen Begriff. Ich würde an deiner Stelle ein EventWaitHandle-Objekt mit EventResetMode.AutoReset verwenden.

Hier mal eine Lösung, die nur ein EventWaitHandle-Objekt für die Instanzerkennung und für das Warten im Thread verwendet. Ich glaube viel kürzer geht es nicht:


using System;
using System.Windows.Forms;
using System.Threading;

abstract class App
{
   private static Form _form;
   private static EventWaitHandle _ewh;

   public static void Main (string [] astrArg)
   {
      bool fCreatedNew = false;

      // rote Ampel erzeugen
      _ewh = new EventWaitHandle (false,
                                  EventResetMode.AutoReset,
                                  "p67agza7ag",
                                  out fCreatedNew);
      if (fCreatedNew) {
         Console.WriteLine ("Erstes Exemplar");

         _form = new Form ();
         IntPtr h = _form.Handle;

         Thread t = new Thread (BringToFrontThread);
         t.Start ();

         Application.Run (_form);

         _form = null;
         // auf grün setzen
         _ewh.Set ();
      } else {
         Console.WriteLine ("Weiteres Exemplar");

         // auf grün setzen
         _ewh.Set ();
      }
   }

   public static void BringToFrontThread ()
   {
      for (;;) {
         // auf grün warten; springt sofort wieder auf rot
         _ewh.WaitOne ();
         if (_form == null) {
            break;
         }
         _form.BeginInvoke (new ThreadStart (BringToFrontMethod));
      }
   }

   public static void BringToFrontMethod ()
   {
      Console.WriteLine ("BringToFront");
   }
}
Das einzige Problem ist, dass ich es nicht geschafft habe, das Fenster in den Vordergrund zu bekommen und zu aktivieren. Peinlicherweise habe ich schon diverse Male an anderer Stelle den untauglichen Tipp gegeben, BringToFront zu benutzen. Es wird also momentan nur ausgegeben, dass das Fenster in den Vordergrund gebracht wird. Der fehlende Code müsste also noch ergänzt werden, damit das Programm funktioniert.


[EDIT=2017]Weiter unten findet sich Code, in dem zusätzlich auch das in den Vordergrund bringen implementiert ist.[/EDIT]

Möglicherweise hilft der Beitrag von miketech aus Andere Instanz sichtbar machen:
Zitat
Im RemoteObject habe ich eine Methode GetWindowHandle(). Die liefert mir das Handle des Hauptfensters zurück. Und anschließend rufe ich den Win32 Call SetForegroundWindow(handle) auf. Damit gehts jetzt.
Statt IntPtr h = _form.Handle; könnte man besser im Konstruktor des eigenen Forms CreateHandle () aufrufen. Das war mir nicht möglich, weil ich im Beispiel der Kürze wegen keine eigene Form-Klasse verwende.

Oder der vielleicht hilft der Beitrag von oli001 in Nur eine Instanz einer Anwendung zulassen :
Zitat
ich habe nun eine Möglichkeit gefunden, mit der die Applikation aufgerufen und in den Vordergrund gebracht werden kann. Das ganze funktioniert über die Windows API.

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



Dabei seit:
Beiträge: 201

Themenstarter:

beantworten | zitieren | melden

@herbivore:

Herzlichen Danke, dass ist genau was ich wollte.

1 Anmerkung
Console und Forms in einer Appl. geht doch nicht gut oder? Bei mir, wenn ich den SourceCode ausfuehre sehe ich die Console nicht.

2 Frage
1. Wenn ich ein Thread erzeuge kann ich denn dann Parameter uebergeben und Rueckgabe werte abfangen?
2. Kann sich ein Thread selbst beenden.
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 stift,
Zitat
Console und Forms in einer Appl. geht doch nicht gut oder?
Klar geht das gut. Wenn du die Anwendung als Windows-Forms übersetzt, werden die Ausgaben nicht angezeigt. Wenn du die Anwendung (in der Testphase) als Konsolenanwendung übersetzt, bekommst du die Ausgaben.
Zitat
Wenn ich ein Thread erzeuge kann ich denn dann Parameter uebergeben und Rueckgabe werte abfangen?
Zu Threads und Parameter: Es geht und es gibt schon diverese Themen hier im Forum dazu.
Rückgabewert geht m.E. nicht direkt, denn die Thread-Routine ist ja void. Aber du hast ja gesehen, dass im Beispiel oben die beiden Threads auf dieselben Variablen zugreifen können. Ist also kein Problem was "zurückzugeben".
Zitat
Kann sich ein Thread selbst beenden.
Klar, er muss einfach return machen oder ans Ende der Thread-Methode kommen.

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



Dabei seit:
Beiträge: 201

Themenstarter:

beantworten | zitieren | melden

1.
??
ich benutze VS 2005

2.
So habe ich es auch bereits geloest.

3.
Habe nicht dran gedacht.
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 stift,
Zitat
ich benutze VS 2005
ist nicht schlimm. :-)

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



Dabei seit:
Beiträge: 14

beantworten | zitieren | melden

Hey,

Ich würde oben stehendes Beispiel gerne in einem VS.NET (Framework 1.1) Projekt nutzen. leider gibt es da noch kein EventWaitHandle. Gibt es da etwas Äquivalentes?

Gruß,
Flo
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 LosWochos,

nein, was wirklich äquivaltentes gibt es nicht. Was am ehesten in die Richtung geht, ist Mutex, denn man braucht ja was benanntes. Prozesslokal wäre AutoResetEvent äquivalent, aber das nützt eben nichts.

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



Dabei seit:
Beiträge: 14

beantworten | zitieren | melden

Hallo herbivore,
Zitat
Original von herbivore
nein, was wirklich äquivaltentes gibt es nicht. Was am ehesten in die Richtung geht, ist Mutex, denn man braucht ja was benanntes. Prozesslokal wäre AutoResetEvent äquivalent, aber das nützt eben nichts.

OK, dann landet ein weiteres Projekt auf der ewigen Liste der Projekte die nach 2.0 portiert werden sollten ;-)

Danke für die Info.

Gruß,
LosWochos
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,

im folgenden beschreibe ich, wie man eine Anwendung bzw. deren Fenster zuverlässig in den Vordergrund bekommt. Dafür ist allerdings ein "Trick" erforderlich.

Normalerweise ist es nicht erlaubt, ein Fenster einer Anwendung, die gerade nicht aktiv ist, so in den Vordergrund zu bringen, dass es gleichzeitig aktiv wird und dadurch den Fokus bekommt.

Das ist auch gut so, denn wenn ein Benutzer gerade irgendwo am Tippen ist, würden seine Eingaben ungewollt in die sich vordrängelnde Anwendung umgeleitet werden. Bis er das Vordrängeln bemerkt hätte wären dann typischerweise schon mehrere Tastendrücke an die sich vordrängelnde Anwendung gegangen.

Versucht eine Anwendung ein Fenster *unerlaubt* in den Vordergrund zu bringen (und dabei zu aktivieren), obwohl der Anwender gerade mit einer anderen Anwendung arbeitet bzw. auch nur wenn eine andere Anwendung aktiv ist, fängt stattdessen die erstgenannte Anwendung in der Titelleiste an zu blinken. Das ist auch gut so und daran ändert auch das folgende nichts.

Nun kann es aber sein, dass der Benutzer will, dass eine andere Anwendung in den Vordergrund kommt, zum Beispiel, weil er eine Anwendung, von der es nur eine Instanz geben soll, erneut startet (siehe [FAQ] mehrere Programminstanzen verhindern (inkl. Parameterübergabe)). Der neu gestartet Prozess soll das dann erkennen und typischerweise die schon laufende Anwendung in den Vordergrund bringen, ganz so wie sie im Vordergrund erscheinen würde, wäre sie gerade zum ersten Mal gestartet worden.

Damit sich die schon laufende Anwendung auch tatsächlich in den Vordergrund bringen kann (denn spätestens durch das erneute Starten ist sie es ja gerade nicht mehr), muss der neue Prozess ihr das erlauben.

Dazu gibt es die Win32 Funktion AllowSetForegroundWindow. (1)

Natürlich darf ein Prozess das nur dann anderen Prozessen erlauben, wenn er zum Zeitpunkt des Aufrufs selber Fenster in den Vordergrund bringen darf, aber das ist im hier zur Diskussion stehenden Szenario automatisch der Fall.

AllowSetForegroundWindow bekommt als Parameter die ID des Prozesses, der seine Fester in den Vordergrund bringen können soll. Der neu gestartet Prozess müsste also die ProzessIds seines schon laufenden Zwillings ermitteln. Es gibt aber auch die Möglichkeit dies allen Prozessen zu erlauben. Ich halte das für vertretbar, weil in unserem Szenario der schon laufende Zwillingsprozess von diesem Recht ohne spürbare Verzögerung Gebrauch macht und damit - nach meinem Verständnis - dieses Recht für die anderen Prozesse sofort wieder erlischt (*).

Das eigentliche in den in den Vordergrund bringen erfolgt durch die zugehörige Win32 Funktion SetForegroundWindow (4) (Die .NET Methode BringToFront ist dazu *nicht* geeignet).

Dabei ist noch zu beachten, dass das fragliche Fenster in den verschiedensten Zuständen sein kann: versteckt (hidden, d.h. Visible = false), minimiert, als zuvor maximales Fenster minimiert oder bereits sichtbar (aber nicht aktiv) oder verdeckt von anderen Fenstern. Um das Fenster aus allen diesen Zuständen wieder so in den Vordergrund zu bringen, wie es zuvor angezeigt wurde, muss mehr passieren. Zum einem muss das Fenster sichtbar sein bzw. werden (2) (Visible = true) und zum anderen muss es nötigenfalls wiederhergestellt werden (3) (Win32 Funktion ShowWindow mit Restore-Parameter).

Nur alle vier Schritte zusammen bewirkt das Gewünschte in jedem denkbaren Fall(**).

Damit man sich um solche Details nicht kümmern muss, habe ich die folgenden Methoden geschrieben. Sie sind für ganz normale Fenster gedacht. Ich habe sie *nicht* mit MDI-Fenstern getestet und es ist auch mehr als fraglich, ob sie dafür funktionieren würden.

Im folgenden Code findet sich eine (Erweiterungs-)Klasse, die die Methoden AllowSetForegroundForAllProcesses und SetForeground (in dem beschrieben umfassenden Sinn) zur Verfügung stellt. Letztere Methode als Erweiterungsmethode der Klasse Form.

Direkt im Anschluss findet sich ein Beispiel zur Verwendung der Methoden, das gleichzeitig als Snippet bzw. Vorlage für das Hauptprogramm einer Anwendung, von der es nur eine Instanz geben soll, verwendet werden kann.

using System;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;

// Methoden um ein ein anderes Fenster (unter dessen Mitwirkung)
// in den Vordergrund zu bringen, egal in welchem Zustand
// (versteckt, minimiert, als maximales Fenster minimiert oder
// bereits sichtbar oder verdeckt von anderen Fenstern)
// sich das (andere) Fenster befindet.
public static class FormExtensions
{
   // Muss aus dem Prozess heraus aufgerufen werden, der aktuell
   // das Recht hat, ein Fenster in den Vordergrund zu bringen.
   public static void AllowSetForegroundForAllProcesses ()
   {
      AllowSetForegroundWindow (ASFW_ANY); // (1)
   }

   // Muss aus dem Prozess heraus aufgerufen werden, 
   // der das Fenster in den Vordergrund bringen will.
   public static void SetForeground (this Form form)
   {
      form.Visible = true; // (2)
      if (form.WindowState == FormWindowState.Minimized) {
         ShowWindow (form.Handle, SW_RESTORE); // (3)
      }
      SetForegroundWindow (form.Handle); // (4)
   }

   private const int ASFW_ANY = -1;
   private const uint SW_RESTORE = 0x09;

   [DllImport("user32.dll")]
   private static extern bool AllowSetForegroundWindow (int processId);

   [DllImport ("user32.dll")]
   private static extern int ShowWindow (IntPtr handle, uint msg);

   [DllImport ("user32.dll")]
   private static extern bool SetForegroundWindow (IntPtr handle);
}

// Beispielhafte Verwendung der zuvor definierten Methoden und zugleich
// Snippet für eine Anwendung, von der es nur eine Instanz geben soll.
static class App
{
   private static Form            _form;
   private static EventWaitHandle _waitForSibling;
   private static volatile bool   _stopThread = false;

   [STAThread]
   public static void Main (string [] args)
   {
      bool createdNew = false;

      // Eine rote Ampel neu erzeugen oder falls schon eine von einem
      // Zwillingsprozess existiert, Zugriff darauf verschaffen
      using (_waitForSibling = new EventWaitHandle (false, EventResetMode.AutoReset, "{717ccfb9-2dbc-4a7d-912f-72f703eb3fb8}", out createdNew)) { // SIEHE ANMERKUNG

         if (createdNew) {
            Console.WriteLine ("Erstes Exemplar");

            _form = new Form ();
            _form.CreateControl ();

            new Thread (DisplaySiblingThread).Start ();

            Application.Run (_form);

            _stopThread = true;
            _waitForSibling.Set (); // Ampel für uns selbst auf grün setzen
            Thread.Sleep (0);

         } else {
            Console.WriteLine ("Weiteres Exemplar");

            FormExtensions.AllowSetForegroundForAllProcesses (); // HIER Schritt 1
            _waitForSibling.Set (); // Ampel für unseren Zwillingsprozess auf grün setzen
         }
      }
   }

   public static void DisplaySiblingThread ()
   {
      for (;;) {
         // Darauf warten, dass die Ampel grün wird.
         // Die Ampel springt anschließend automatisch wieder auf rot
         _waitForSibling.WaitOne ();
         if (_stopThread) { return; }
         _form.BeginInvoke (new Action (_form.SetForeground)); // HIER Schritt 2-4
      }
   }
}
Achtung: Die GUID {717ccfb9-2dbc-4a7d-912f-72f703eb3fb8} ist nur ein Beispiel und muss bei der Übernahme des Codes unbedingt durch eine eigene GUID oder einen anderen weltweit eindeutigen String ersetzt werden.

herbivore

(*) Wer auf Nummer sicher gehen will, muss die ProzessIds des Zwillings ermitteln und diese als Parameter an AllowSetForegroundWindow übergeben.

(**) In keiner anderen Stelle im Netz habe ich Code gefunden, der alle denkbaren Fälle berücksichtigt. Immer fehlt mindestens einer der vier nötigen Schritte.
private Nachricht | Beiträge des Benutzers