Kontakt
DSVGO
Historie | |
---|---|
21.07.2003 | Deutlich sinnvolleres Beispiel zur korrekten Anwendung; Code verschönert |
16.08.2001 | Berücksichtigung von nicht MDI-Anwendungen (Hinweis von Walter Kiffe) |
07.05.2001 | Erste Version |
Nachfolgend wird eine Möglichkeit vorgestellt, das Programm-Handle einer bereits laufenden Anwendung zu bestimmen. Dies kann z.B. dafür genutzt werden, das Programm zu aktivieren, z.B. wenn versucht wurde, eine zweite Instanz zu starten. Natürlich sind weitergehende Manipulationen jeglicher Art denkbar...
Mit der VB-Eigenschaft App.PrevInstance kann festgestellt werden, ob das VB-Programm bereits läuft. Falls Ja, so ist es oft erwünscht, dass das gerade gestartete Programm beendet wird.
Als Service kann ggf. mit AppActivate diese vorige Instanz aktiviert werden. Leider nimmt dieser Befehl einfach den ersten Treffer (gemäß der Caption), egal ob es sich überhaupt um ein VB-Programm handelt. Dadurch werden oft die falschen Programme nach vorne geholt. Ausserdem können mit AppActivate keine Programme aktiviert werden, die minimiert wurden (d.h. auf der TaskBar liegen).
Mit den unten gezeigten Routinen kann dieses Problem quasi mit einer einzigen Zeile gelöst werden. Dazu muss in Sub Main an der passenden Stelle einfach nur die Routine PrevActivate aufgerufen werden:
Public Sub Main() If App.PrevInstance Then PrevActivate Else 'Eigentliche Anwendung, etwa: frmMain.Show vbModal End If End Sub
Tipp: Vergessen Sie bitte nicht, in den Projekt-Eigenschaften das Start-Objekt auf "Sub Main" zu stellen!
Dadurch wird das oben beschriebene gewünschte Verhalten realisiert, d.h. falls schon eine Programm-Instanz läuft, wird das neu gestartete Programm sofort wieder beendet.
Die eigentliche Logik steckt in der weiter unten gezeigten ApplActivate-Routine. Hier wird nur getestet, ob überhaupt eine andere Instanz läuft. Nur in diesem Fall wird die aktuelle Überschrift (Default) als Suchbegriff benutzt. Damit das Programm sich nicht selbst findet, wird vorher zur Sicherheit die Caption auf einen zufälligen Wert gesetzt:
Public Sub PrevActivate( _ Optional ByVal Title As String _ ) 'Checken, ob Aktivierung notwendig: If Not App.PrevInstance Then Exit Sub 'Caption merken und maskieren: If Len(Title) = 0 Then _ Title = Screen.ActiveForm.Caption If Not Screen.ActiveForm Is Nothing Then _ Screen.ActiveForm.Caption = CStr(Rnd) 'Andere Instanz aktivieren: ApplActivate Title End Sub
Man kann es sich leicht vorstellen: Ohne intensiven API-Einsatz geht es mal wieder nicht... Als besonders aufwändig stellt sich die Suche nach dem geeigneten Windows-Handle heraus:
Der folgende Code-Abschnitt ist im Deklarationsteil eines Moduls oder Formulars einzufügen:
Private Declare Function FindWindowA Lib "user32" ( _ ByVal lpClassName As String, _ ByVal lpWindowName As String) As Long Private Declare Function GetParent Lib "user32" ( _ ByVal hwnd As Long) As Long Private Declare Function GetWindow Lib "user32" ( _ ByVal hwnd As Long, ByVal wCmd As Long) As Long Private Declare Function GetWindowTextA Lib "user32" ( _ ByVal hwnd As Long, ByVal lpString As String, _ ByVal cch As Long) As Long Private Declare Function IsIconic Lib "user32" ( _ ByVal hwnd As Long) As Long Private Declare Sub SetForegroundWindow Lib "user32" ( _ ByVal hwnd As Long) Private Declare Sub ShowWindow Lib "user32" ( _ ByVal hwnd As Long, ByVal nCmdShow As Long)
Die ApplActivate-Prozedur wird entweder mit einem Windows-Handle oder einer Caption aufgerufen. Zu einer Caption wird ggf. das entsprechende Handle gesucht. Falls das Programm minimiert ist, wird es erst wiederhergestellt, bevor es aktiviert wird:
Sub ApplActivate(ByVal Appl As Variant) Const SW_RESTORE = 9 'Ggf. Handle zu Caption suchen: If Not IsNumeric(Appl) Then _ Appl = ApplHandle(Appl) 'Ggf. "Wiederherstellen": If IsIconic(Appl) Then _ ShowWindow Appl, SW_RESTORE 'Anwendung in den Vordergrund bringen: SetForegroundWindow Appl End Sub
Die folgende Funktion sucht zu einer Caption das passende Handle. Dabei wird die Suche in einer bestimmten Klassen-Reihenfolge durchgeführt: Erst werden VB-MDI-Formulare gesucht, dann normale VB-Formulare, und erst dann werden andere Anwendungen/Klassen berücksichtigt.
' ©2003 by Jost Schwider, http://vb-tec.de/ Function ApplHandle(ByVal Caption As String) As Long Dim vClass As Variant 'VB-Applikationen/Klassen bevorzugen: For Each vClass In Array( _ "ThunderRT5MDIForm", "ThunderRT6MDIForm", _ "ThunderRT5Form", "ThunderRT6Form", _ vbNullString) 'Applikation/Klasse checken: ApplHandle = GetHandle(vClass, Caption) If ApplHandle Then Exit Function Next vClass End Function
Die GetHandle-Funktion sucht zu einer Klasse und einer Caption das passende Handle. Bei der Suche werden nur die Fenster berücksichtigt, welche den Desktop als Vater haben (also echte Anwendungen sind):
' ©2003 by Jost Schwider, http://vb-tec.de/ Function GetHandle( _ ByVal Class As String, _ ByVal Caption As String _ ) As Long Const GW_HWNDNEXT = 2 Dim Buffer As String Dim Length As Long 'Auf exakten Treffer checken: GetHandle = FindWindowA(Class, Caption) If GetHandle Then Exit Function 'Alle Klassen-Windows durchlaufen: Caption = UCase$(Trim$(Caption)) GetHandle = FindWindowA(Class, vbNullString) Do While GetHandle 'Nur Top-Windows berücksichtigen: If GetParent(GetHandle) = 0 Then 'Caption holen: Buffer = Space$(255) Length = GetWindowTextA(GetHandle, Buffer, 255) Buffer = UCase$(Left$(Buffer, Length)) 'Exakter Vergleich: If Buffer = Caption Then Exit Do 'MDI-Form berücksichtigen: If Buffer Like Caption & " - *" Then Exit Do End If GetHandle = GetWindow(GetHandle, GW_HWNDNEXT) Loop End Function
© Jost Schwider, 07.05.2001-21.07.2003 - http://vb-tec.de/appactiv.htm