Visual Basic - Technik, FAQ, Tricks, Beispiele

Home / System / Windows / Activate

Laufende Anwendungen aktivieren

Impressum
Kontakt
DSVGO
Historie
21.07.2003Deutlich sinnvolleres Beispiel zur korrekten Anwendung; Code verschönert
16.08.2001Berücksichtigung von nicht MDI-Anwendungen (Hinweis von Walter Kiffe)
07.05.2001Erste Version

Einleitung

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... Smile!

Problem

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).

Beispiel

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! Smile!

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.

Lösung

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

Code / Quelltext

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:

API-Deklarationen

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)

Anwendung aktivieren

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

Handle zu Caption suchen

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

Handle zu Klasse/Caption suchen

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