VB6-Mehrfachstart verhindern u. 1.Inst. aktivieren

Hallo VB-Experten,
ich habe ein kleines Problem.
Ich möchte verhindern, dass ein Programm mehrfach gestartet wird (klappt) und dann die bereits laufende 1. Instanz der Anwendung in den Vordergrund bringen (klappt nicht).

Der bisher verwendete Code in einer Form mit dem Namen Form1 sieht so aus, die Form enthält nur einen Button Command1:

_ **' benötigte API-Deklarationen**
Private Declare Function FindWindow Lib "user32" Alias \_
 "FindWindowA" (ByVal lpClassName As String, \_
 ByVal lpWindowName As String) As Long




Private Declare Function SetForegroundWindow Lib \_
 "user32" (ByVal hwnd As Long) As Long




Private Sub Command1\_Click()
 End
End Sub




Private Sub Form\_Load()
 Dim RetVal As Long




 **' Prüfen, ob Anwendunng bereits gestartet**
 If App.PrevInstance Then
 **' Ja! Jetzt Fenster-Handle ermitteln**
 RetVal = FindWindow(vbNullString, "Form1")
 If RetVal 0 Then
 **' Anwendung gefunden - jetzt aktivieren**
 MsgBox "Anwendung läuft bereits!" + vbCrLf + \_
 "Die 2. Instanz wird wieder beendet!" + \_
 vbCrLf + "(oder sollte beendet werden :frowning: )", \_
 vbOKOnly + vbInformation
 Call SetForegroundWindow(RetVal)
 End If
 **' diese (zweite) Instanz beenden**
 End
 End If




End Sub_

Das mit dem Beenden der 2. Instanz funktioniert, d.h., es wird richtig erkannt, wenn das Programm schon läuft.
Lieder klappt aber das Hervorholen des Fensters der 1. Instanz nicht. Also funktioniert entweder FindWindow oder SetForegroundWindow nicht.
Als zusätzliche Info: Ich verwende VB6 unter Win2000.
Bitte bitte, sagt mir, was ich falsh mache!

Hallo auch!

Das Problem liegt nicht daran, dass FindWindow() oder SetForegroundWindow() nicht funktionieren, sondern daran, dass SetForegroundWindow() _so_ funktioniert wie es das tut.
Unter Win95 ging es noch anstandslos, mit Win98/ME/2000/XP/2003 funktioniert SetForegroundWindow() nur noch mit zusätzlichem Voodoo. Anderenfalls blinkt das aktivierte Fenster nur 3* in der Taskleiste (ist das der Effekt, den Du beobachtest?).
Das ist u.a. auch im MSDN bei SetForegroundWindow dokumentiert.

Letztlich läuft es darauf hinaus, dass sich die erste Instanz mit AttachThreadInput an das bereits laufende Fenster 'ranhängen muss, _dann_ erst das SetForegroundWindow() funktioniert und sich anschließend wieder abkoppeln muss…
Z.B. hier: http://www.thescarms.com/vbasic/alttab.asp ist der Prozess noch etwas detaillierter beschrieben, ansonsten kannst Du mal nach „SetForegroundWindow AttachThreadInput“ googlen, dann wirst Du fündig.

Gruß,
Martin

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

hallo klaus,

soweit ich weiß, kannst du die fenster und vordergrundanzeige nur über api-calls steuern.

für das starten nur einer instanz-- gibt es da nicht eine eigenschaft in den optionen von vb6 ???

gruß

rasta

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hallo Klaus,

Starte deine Programme über die Sub Main. Die schreibst du in ein Modul.

In dieser Sub schreibst du dann folgenden Code:

Sub Main()
dim OldTitle$
If App.PrevInstance then
OldTitle=App.Title
App.Title=„XYZ“
AppActivate OldTitle
End
End If
Form1.Show
End Sub

Sollte eine Vorgaenger - Instanz gefunden werden: Kein Problem, es wird einfach zu dieser umgeschalten, danach beendet sich das Programm selbst :smile:

VLG Alex

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Herzlichen Dank an alle!

D :smiley:

@Alex: Das ist die genialste weil einfachste und funktionierende Lösung! So werde ich es machen. :smile:
Allerdings verstehe ich nicht ganz, warum das nur in der sub Main eines Moduls funktioniert, das macht es dann u.U. wieder unnötig kompliziert.
Aber egal, so ist es für mich brauchbar.

Ciao

Klaus

hallo klaus,

ja alex-lösung ist die beste für deinen fall.
mein posting gilt, wenn du fremde fenster kontrollieren möchtest.

gruß

rasta

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hallo Klaus,

mir ist gerade aufgefallen, das wenn das programm minimiert ist( in der Taskbar liegt), dann der Laufzeitfehler 5 auftritt was sehr unschön ist :frowning:
Ist das Programm nicht minimiert so geht es.
Wenn du diesen Effekt umgehen willst, so bedarf es einer menge Schreibarbeit.
Du musst es wissen, ob dieser Fall eintreten kann.

Hier eine andere Lösung für das Problem

'API Declaration

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)

Public Sub Main()
If App.PrevInstance Then
PrevActivate
Else
'Eigentliche Anwendung, etwa:
Form1.Show vbModal
End If
End Sub

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

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

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

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

Wie du nun siehst, macht sich ein Modul doch besser als den ganzen Text in die Form zu tipseln.
Auf deine Frage, warum du dies in der Sub Main schreiben sollst.
Ich bin davon ausgegangen das du deine Programme immer in der Sub Main startest. Ich persoenlich mache das immer, da ich zum Programmstart meistens noch aufgaben zu erledigen habe, bevor das erste Fenster angezeigt wird. Bsp. Überprüfen welches OS, Werte einlesen / Initialisieren, Prüfen ob der Rechner im abgesicherten Modus etc gestartet ist. Desweiteren schreibe ich in das Modul meine ganzen allgemeinen Functionen / Prozeduren. So ist es meiner Meinung nach ein wenig übersichtlicher. Aber das ist alles geschmackssache :smile:

Du kannst natuerlich auch meine Variante in die Form_Load() Prozedure in der Form1 schreiben. Sollte eigentlich auch gehen :smile:

VLG Alex

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]