Zitat:
tut mir leid das is mir zu hoch
Hast du überhaupt mal versucht meinen Vorschlag zu testen und zu begreifen ?
In deinem Source gibt es gleich mehrere Probleme.
1.) CreateMutex() kann fehlschlagen wenn unter
Win9x der Prozess der diesen
Mutex angelegt hat gecrasht ist. Dann besteht dieser
Mutex im System weiterhin obwohl es diesen Prozess nicht mehr gibt. Normalerweise würde eine Anwendung die auf CreateMutex() aufbaut dann nicht mehr starten. In deinem Falle würde die 1. Instance dann per EnumWindow() nach dem Fenster suchen, aber keines finden. Somit ist der CreateMutex() Aufruf sinnlos.
2.) Benötigt man zwecks Datenaustausch von ParamStr() ein Fensterhandle dann kann auch CreateMutex() verzichtet werden.
3.) Daten die von einem Prozess zu einem anderen Prozess, über Fensterhandles, ausgetauscht werden sollen müssen per wm_CopyData gesendet werden. wm_CopyData stellt sicher das der Speicherbereich in TCopyDataStruct aus der Gültigkeit des sendenden Prozesses auch gültig im empfangenden Prozess ist. Deshalb wird wm_CopyData immer NUR mit SendMessage() benutzt. Und deshalb funktioniert dein SendMessage() eben nicht.
4.) Dein EnumWindows() kann leicht durch FindWindow() ersetzt werden.
Statt also mit EnumWindows() über alle TopLevel Fenster zu iterieren reicht FindWindow(ClassName, WindowText) völlig aus.
5.) die Überprüfung in .FormShow() und das Erzeugen des
Mutex ist an dieser Stelle denkbar ungünstig. Der beste Zeitpunkt, und auch der frühestmögliche, ist innerhalb einer
Unit-Initialization. Wird meine obige
Unit im Projektquelltext an 1. Stelle eingefügt dann wird sie noch vor der Erstellung jeglicher Forms ausgeführt. Damit würde also die 2. Instance der Anwendung erst garnicht langwierige Initialisierungen durchführen.
Aber im Grunde macht mein Vorschlag genau das was du versucht zu erreichen.
1.) TSingleInstance.Start sucht im System nach einem Fenster vom Typ "TPUtilWindow" + Caption "my_programXYZ12345". Dies erhöt die Sicherheit.
2.) wenn es gefunden wird wird der aktuelle ParamStr() = GetCommandLine an dieses Fenster per wm_CopyData gesendet. Wir senden also nicht nur ParamStr(1) sondern die komplette Kommandozeile.
3.) sollte das Fenster korrekt antworten so wird die aktuelle Anwendung angehalten und terminiert
4.) sollte kein Fenster "PrevWnd" gefunden worden sein, oder das Fenster hat nicht korrekt geantwortet dann wird die aktuelle Anwendung zur 1. Instance. Sie erzeugt ein Fenster vom Typ "TPUtilWindow" + Caption "my_programxyz123445"
5.) TSingleInstance.WndProc() wartet nun auf die Message wm_CopyData die durch eine 2. Instance der Anwendung aufgerufen wird. Punkte 1.) bis 3.) oben.
6.) sollte TSingleInstance.WndProc() eine gültige wm_CopyData Message erhalten so setzt sie die globale Variable CmdLine auf die Daten die per wm_CopyData empfangen wurde. CmdLine entspricht in diesem Moment der Kommandozeile der zweiten Instance der Anwendung.
7.) nun wird TSingleInstance.OnStartup() aufgerufen, die DEINEN Code enthalten sollte. Innerhalb .OnStartup() kannst du über die Methoden .ParamCount und .ParamStr() auf die Kommandozeile der zweiten Anwendung zugreifen.
8.) TSingleInstance.Stop wird bei der Terminierung der Anwendung aufgerufen und zerstört unser Helperfenster.
So was ist daran kompliziert ? besonders weil es auch sauberer und lesbarer Source ist !
D.h. für dich:
1.) füge
Unit Singleinstance zu deinem Projekt hinzu
2.) ändere den Wert von sTitle auf einen eindeutigen String
3.) ändere den Code in .OnStartup so wie du ihn brauchst. Innerhalb von .OnStartup nutzt du ParamCount und ParamStr() so als würdest du ganz normal damit wie in deiner Anwendung arbeiten.
Das wars.
Gruß Hagen