|
Die Eigenschaft PrevInstance des App-Objekts teilt Ihnen mit, ob bereits eine andere Instanz Ihrer Anwendung läuft. Wenn Sie nun bei einem erneuten Start Ihrer Anwendung eine solche vorhergehende Instanz lediglich aktivieren möchten, anstatt die Anwendung zum zweiten Mal auszuführen, bedarf es einiger Kunstgriffe. Visual Basic hält dazu unverständlicherweise leider keine eigenen Bordmittel bereit.
Eine relativ bekannte Möglichkeit ist, über eine Reihe von API-Funktionen ein Fenster (Form) der vorhergehenden Instanz ausfindig zu machen und zu aktivieren, und es gegebenenfalls wiederherzustellen, falls es minimiert sein sollte. In der Regel funktioniert dies auch ganz gut. Allzu sicher ist das Verfahren jedoch nicht, da zwischen der bereits laufenden Instanz und der sich um die Reaktivierung derselben bemühenden neuen Instanz keinerlei direkte Beziehung besteht. Zum einen könnten Sie unabsichtlich eine völlig andere Anwendung reaktivieren, falls zufällig verschiedene Namensübereinstimmungen zu Ihrer Anwendung bestehen sollten. Zum anderen kann die neue Instanz nichts über den Zustand der laufenden Anwendung wissen. Sie kann vielleicht vergeblich nach dem zu reaktivierenden Form suchen, während dieses gar nicht geladen ist. Oder dieses Form ist zufällig gesperrt, weil der Anwender gerade ein anderes Form Ihrer Anwendung modal geöffnet hat. Je nach Komplexität Ihrer Anwendung können weitere Unwägbarkeiten hinzukommen, über die sich die neu startende Instanz nur mit (für VB-Verhältnisse) recht hohem Aufwand über API-Mechanismen informieren könnte. Auch ist eine gezielte Kommunikation zwischen den beiden Instanzen, etwa Übergabe der Kommandozeilen-Parameter der neuen Instanz an die alte und dergleichen, nicht so einfach zu erreichen. Ich will daher auf diesen Weg gar nicht erst weiter eingehen.
Mit nur minimalem Aufwand können Sie hingegen über ActiveX einen sicheren und sehr flexiblen Mechanismus etablieren, mit direkter Verständigung zwischen der laufenden und jeder neu hinzukommenden Instanz. Und natürlich mit problemloser Übergabe der Kommandozeilen-Parameter von der neuen an die laufende Instanz.
Dazu verwandeln Sie Ihre eigentliche Anwendung einfach in eine ActiveX-EXE und schalten eine kleine Starter-Anwendung (weiterhin eine gewöhnliche Standard-EXE) vor. Diese Starter-EXE erweckt die ActiveX-EXE beim ihrem ersten Aufruf zum Leben. Bei jedem weiteren Aufruf reaktiviert sie nur noch die bereits laufende Instanz der ActiveX-EXE, und kann mit ihr beliebig Informationen austauschen oder Anweisungen an sie absetzen.
Schauen wir uns zunächst einmal das einfachste Modell einer solchen Konstruktion an. Ihre zur ActiveX-EXE mutierte Anwendung benötigt zusätzlich nichts weiter, als eine öffentliche und instanzierbare Klasse - wie jede andere ActiveX-EXE. Diese Klasse, nennen wir sie "Instance", braucht noch nicht einmal über Methoden und Eigenschaften zu verfügen. Lediglich ihre Class_Initialize-Prozedur wird benötigt. In dieser laden Sie das Hauptform (Form1, oder wie Sie es auch immer genannt haben mögen) der Anwendung, falls es noch nicht geladen sein sollte. Und hier reaktivieren sie es gegebenenfalls und stellen es wieder her, falls es minimiert sein sollte.
Private Sub Class_Initialize()
If Forms.Count = 0 Then
Load Form1
End If
With Form1
If .WindowState = vbMinimized Then
.WindowState = vbNormal
End If
.Show
End With
End Sub
Die Starter-EXE besteht nur aus einem Standard-Modul mit einer Sub Main-Prozedur. Dazu nehmen Sie einen Verweis auf Ihre ActiveX-EXE-Anwendung auf. Beachten Sie, dass die Klasse Instance separat deklariert und instanziert werden muss. Bei einer Deklaration wie
Dim nInstance As New Instance
würde nämlich nicht sofort deren Class_Initialize-Prozedur aufgerufen, sondern erst bei einem Zugriff auf eine Methode oder Eigenschaft der Klasse - über die diese ja gar nicht verfügt.
Public Sub Main()
Dim nInstance As Instance
Set nInstance = New Instance
End Sub
Das war nun auch schon alles. Der einzige Unterschied zu Ihrer früher "normalen" Anwendung besteht darin, dass Sie bzw. die Anwender diese nicht mehr direkt starten, sondern statt dessen nur noch die Starter-EXE. Natürlich sollte sich diese auch mit dem eigentlichen Icon Ihrer Anwendung darstellen - wie das ohne ein Form in der Starter-EXE zu bewerkstelligen ist, können Sie in "Icon aus dem Nichts" nachlesen.
Falls es jedoch weiterhin möglich sein soll, trotzdem ganz bewusst eine zweite Instanz Ihrer Anwendung zu starten, rufen Sie die Starter-EXE mit einem Kommandozeilen-Parameter (beispielsweise "/X") auf. In ihrer Sub Main-Prozedur rufen Sie in diesem Fall die ActiveX-EXE als Standalone-Anwendung auf (beide EXEs sollten sich der Einfachheit halber im gleichen Verzeichnis befinden).
Public Sub Main()
Dim nInstance As Instance
If UCase$(Command$) = "/X" Then
Shell "Anwendung.exe", vbNormalFocus
Else
Set nInstance = New Instance
End If
End Sub
Damit auch dann das Hauptform der Anwendung geladen und angezeigt wird, braucht nun auch die ActiveX-EXE ein Standard-Modul mit einer Sub Main-Prozedur.
Public Sub Main()
If App.StartMode = vbSModeStandalone Then
Form1.Show
End If
End Sub
Bis jetzt fehlt allerdings noch die Möglichkeit des Informationsaustauschs zwischen Ihrer als ActiveX-EXE laufenden Anwendung und einer später gestarteten Starter-EXE.
Eine wie bisher sozusagen "stumme" Verbindungsklasse braucht ja schließlich nicht zu sein. Sie können die Klasse Instances mit einer Methode versehen, der sie die Kommandozeile als Parameter übergeben. In dieser Methode haben Sie den vollen Zugriff auf Ihre Anwendung - über die Forms-Collection auf alle geladenen Forms und auf globale Variablen. Hier können Sie nach Herzenslust die Kommandozeile auswerten und anstellen, was immer Sie wollen.
Public Function Start(Params As String) As Variant
'...
End Sub
Sie können auch den bisherigen Inhalt der Class_Initialize-Prozedur hierher verschieben und so etwa gegebenenfalls anhand der übergebenen Kommandozeile entscheiden, welches Form (re)aktiviert werden soll. So können Sie auch schon beim ersten Start die Kommandozeile nach Belieben auswerten.
In der Sub Main-Prozedur rufen Sie dann eben noch zusätzlich diese Methode auf:
Public Sub Main()
If UCase$(Command$) = "/X" Then
Shell "Anwendung.exe", vbNormalFocus
Else
With New Instance
.Start Command$
End With
End If
End Sub
Die Methode Start der Instance-Klasse ist hier einfach rein vorsorglich als Funktion ausgelegt. Sie können Sie natürlich auch schlicht als Sub-Prozedur anlegen, wenn Sie keinen Bedarf dafür haben, der Starter-EXE irgendwelche Informationen zurückzugeben, die dort nachträglich noch ausgewertet werden und zu weiteren Aktionen (etwa einer Meldung für den Anwender) führen könnten.
|