# Alle offenen Fenster schliessen



## DrMueller (15. Juni 2007)

Hallo mal wieder,
folgendes Problem: Ich programmiere ein Modul zu einem Hauptprogramm. Jetzt hätte ich gern, dass, falls das Hauptprogramm sich schliesst, alle meine Nebenfenster auch schliessen.
Den Event, bevor das Hauptprogramm schliesst, habe ich, aber wie kann ich testen, welche Fenster offen sind und diese ggf. schliessen?
Ist das überhaupt möglich?


----------



## DrSoong (15. Juni 2007)

Du könntest über die _Forms-Auflistung_ alle Forms abfragen und die, die geladen sind mittels _Unload_ schließen. Das ganze kommt am besten in das _Form_Unload_-Event deines Hauptforms und wird damit beim Schließen derselben ausgeführt.

Links dazu:
Forms-Auflistung
Form-Objekt


Der Doc!


----------



## DrMueller (15. Juni 2007)

Welche Forms sind denn in diesem Forms-Objekt? Egal, wieviele ich offen habe, ich bekomme bei Count immer 0, auch wenn ich jedes einzeln abfragen will, bekomme ich kein Form zurück, obwohl viele offen sind.


----------



## ronaldh (15. Juni 2007)

Das machst Du so:


```
Private Sub Form_Unload(Cancel As Integer)
   Dim frm As Form
   For Each frm In Forms
      Unload frm.Name
   Next
   
End Sub
```

Viele Grüsse
ronaldh


----------



## flyandshot (15. Juni 2007)

es gäbe auch die ganz heftige Methode mit shell ("taskkill /IM der-name-deines-programms.exe")...
wird allerding nicht wirklich so gern gesehen. Außerdem funktioniert es nicht auf allen Betriebsystemen...


m.f.G.: flyandshot


----------



## DrMueller (18. Juni 2007)

Forms wäre schon gut, aber wenn ich z.B. vor dem Loginwechsel ein Forms.count mache, zeigts mir 0 an, obwohl ich das offene Fenster ja selber sehe. Darum wollte ich wissen, wann genau ein Fenster als offen zählt.


----------



## ronaldh (18. Juni 2007)

Mit folgender Prozedur werden alle geladenen Formen angezeigt:


```
Public Sub Test()
   Dim frm As Form
   For Each frm In Forms
      Debug.Print frm.Name
   Next
   Debug.Print Forms.Count
End Sub
```

Es werden alle Formen angezeigt, die im Speicher geladen sind, ob sichtbar oder nicht.

Das heißt, wenn Du z.B. in Deinem Code irgendwo drinstehen hast:

```
form2.Caption = "Hallo"
```
dann ist Form2 geladen, ob sie nun sichtbar ist, oder nicht. Oder wenn Du eine Form mt der Hide-Funktion (anstatt Unload) verlässt, ist sie zwar nicht mehr zu sehen, aber trotzdem im Speicher geladen.

Grüsse
ronaldh


----------



## DrMueller (18. Juni 2007)

Die Sache scheint kompelxer zu sein als ich dachte, da in Forms nur die Fenster des aktiven Dlls sind. Das Prob ist natürlich, das so meine Fenster nicht angezeigt werden, da die beiden  Events vom Hauptprogramm, worauf ich keinen Zugriff habe, ausgeführt werden. Darum gibts auch immer 0 offene Forms zurück. Kann man das übergreifend machen?


----------



## ronaldh (18. Juni 2007)

Hallo,

Du kannst den Titel eines Fensters abfragen mit einer API-Funktion:


```
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Public Function IsProgramActive(PrgFinm As String) As Boolean
    Dim WinWnd As Long
    WinWnd = FindWindow(vbNullString, PrgFinm)
    If WinWnd = 0 Then
      IsProgramActive = False
   Else
      IsProgramActive = True
   End If

End Function
```

Wenn Du diesen Code in ein Modul packst, und dann die Funktion mit der Überschrift aufrufst, bekommst Du True oder False zurück. Dazu muss jedoch die ganze Titelleiste des abzufragenden Programms abgefragt werden, z.B.

IsProgramActive("tutorials.de - Antworten - Windows Internet Explorer")

Hilft das weiter?

Grüsse
ronaldh


----------



## DrMueller (19. Juni 2007)

Perfekt, genau das was ich brauche.


----------



## DrMueller (19. Juni 2007)

merkwürdig, mittels closeWindow wird das Fenster nur minimiert, gibts so was wie terminateWindow?


----------



## ronaldh (19. Juni 2007)

Da es sich um ein fremdes Programm handelt, könntest Du, wie von flyandshot oben erwähnt, die Taskkill-Methode anwenden. Ist aber, wie er schon sagte, wirklich ziemlich heftig...

Grüsse
ronaldh


----------



## ronaldh (19. Juni 2007)

Es gibt aber auch eine API-Funktion TerminateProcess, mit der Du andere Anwendungen beenden kannst.

Grüsse
ronaldh


----------



## DrMueller (20. Juni 2007)

Das Problem ist ja, das die Dlls vom Hauptprogramm aufgerufen werden, wenn ich als ctrl + alt + del drücke, erscheint nur das Hauptprogramm, meine Fenster werden gar nicht als Tasks behandelt, oder seh ich das falsch? Ich hab den ApiViewer 2004 runtergeladen, scheint laut diesem tatsächlich kein Fensterkiller zu geben. Schade.


----------



## ronaldh (20. Juni 2007)

Dass man einzelne DLL's killen kann, kann ich mir nur schwer vorstellen, da man damit ja möglicherweise einem Programm den Boden unter den füssen weg zieht.

Die einzelnen Prozesse, die aktiv sind, kannst Du Dir mit folgender Funktion ansehen:


```
Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Long, ByVal lparam As Long) As Boolean
Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As Long) As Long
Public Function EnumWindowsProc(ByVal hwnd As Long, ByVal lparam As Long) As Boolean
    Dim sSave As String, ret As Long
    'frmWinList.List1.Clear
    ret = GetWindowTextLength(hwnd)
    sSave = Space(ret)
    GetWindowText hwnd, sSave, ret + 1
    'frmWinList.List1.AddItem Str$(hwnd) + " " + sSave
    If sSave <> "" Then
      frmWinList.List1.AddItem sSave
   End If
    'continue enumeration
    EnumWindowsProc = True
End Function



Public Sub ShowPrograms(Finm As String) 
   Dim iSu As Long
   frmWinList.AutoRedraw = True
   frmWinList.List1.Clear
   EnumWindows AddressOf EnumWindowsProc, ByVal 0&
   For iSu = 0 To frmWinList.List1.ListCount - 1
      frmWinList.List1.ListIndex = iSu
   Next
End Sub
```

Dazu habe ich mir eine Form "frmWinList" gemacht, mit einer Listbox drauf.

Der normale Windows-Taskmanager (Ctrl-Alt-Del) zeigt bei weitem nicht alles was läuft. Da ist der Process Explorer von Syinternals besser. 

Vielleicht hilft Dir das ja weiter!

Grüsse
ronaldh


----------



## DrMueller (20. Juni 2007)

Hm, ich versuchs mal zu erkläre, falls ich irgendwo einen Mist sage, einfach korrigieren bitte:
1. Hauptprogramm wird geöffnet --> Ist ein Prozess, der bleibt.
2. Fenster von mir, die als Schnittstelle dazu programmiert wurden, werden geöffnet --> Kein Prozess, nur Fenster. 
3. Mittels Fenster können verschiedene Arbeiten erledigt werden --> nicht relevant.
4. Da die Fenster nicht modal sind, kann das Hauptprogramm, während meine Fenster offen sind, geschlossen werden.
5. Bei diesem Fall erscheint der Standard-Fehler, dass das Hauptprogramm nicht reagiert.

Dies erscheint nur bei einem Fenster, weswegen ich dieses also schliessen möchte, bevor das Hauptprogramm geschlossen wird.
Falls ich dies bewerkstelligen könnte, würde das auch gleich ein anderes Problem lösen, nämlich, dass alle Fenster schliessen, wenn der User umloggt.
hWnd kann über den Fensternamen, der konstant ist, ermittelt werden. 
Da dieses Fenster kein Task ist, kann ichs es nicht mit Taskkill etc. beenden.
CloseWindow minimiert das Fenster nur, was nichts hilft, denn der Fehler erscheint weiter.

Ich hoffe, ich konnte das Problem hinreichend erklären, kurz gesagt.
Prozess killen hilft nicht.
Fenster minimieren hilft nicht.
Die erfoderlichen Werte sind da nur die Funktion, um ein Fenster zu schliessen, fehlt.

Wer Rechtschreibefehler findet, darf sie behalten.


----------



## ronaldh (20. Juni 2007)

Du rufst Deine Fenster aber aus dem Hauptprogramm heraus auf, oder verstehe ich das falsch?

Wenn dies so ist, dann kannst Du doch im Form_Unload-Ereignis des Hauptprogrammes die gegebenenfalls geöffneten Fesnter schliessen (das hatte ich ja weiter oben geschildert).

Oder rufst Du Deine Fenster mit einem ganz anderen Programm auf? In diesem Fall könntest Du in diesem Programm einen Timer einbauen, der in regelmäßigen Abständen guckt, ob das Hauptprogramm noch da ist, und wenn nicht, alle abhängigen Prozesse schließt. Das kannst Du natürlich auch auf jedem Fenster separat tun, hängt davon ab, wie die Fenster in Zusammenhang stehen.

Grüsse
ronaldh


----------



## DrMueller (20. Juni 2007)

Da sowohl das Hauptorgramm sowie meines aus mehreren Dlls besteht, sind nur die beiden Verbindungs-Dlls miteinander verknüft, ausserdem kann ich in das Hauptprogramm nicht direkt reinprogrammieren, ich bekomme lediglich Events, einer davon ist eben BeforeProgrammClose. 

Das mit dem Forms.Counter funktioniert deswegen nicht, weil beim Schliessen ja die DDL des Hauptprogramms geladen wird und Forms zählt nur die Fenster des eben geladenen Dlls soviel ich weiss.


----------



## ronaldh (20. Juni 2007)

Jetzt bin ich ein wenig verwirrt.



> Das mit dem Forms.Counter funktioniert deswegen nicht, weil beim Schliessen ja die DDL des Hauptprogramms geladen wird



Wenn das Hauptprogramm geschlossen wird, läd es eine DLL?

Wie auch immer: Du willst doch Deine Anwendung schliessen, falls das Hauptprogramm geschlossen wurde. Die Existenz des geöffneten Hauptprogramms kannst Du doch abprüfen.

Wenn Du dies in einem Timer regelmäßig machst, müsstest Du doch Deine Applikation schliessen können! Oder vestehe ich da was vollkommen falsch?

Grüsse
ronaldh


----------



## DrMueller (20. Juni 2007)

Das ganze Problem ist: Wie schliesse ich ein Fenster (nicht minimieren, schliessen) über eine API-Funktion. Nur Fenster, weder Task noch Thread, nur Fenster, aber das scheints laut  ApiViewer gar net zu geben, oder ich bin ein schlechter Sucher, was auch möglich ist.


----------



## DrMueller (20. Juni 2007)

AH endlich gefunden, absolut per Zufall drüber gestolpert.
Ich lese mittels FindWindow die hWnd aus und mittels DestroyWindow kille ich das Fenster. 
Naja danke für die Mühe.


----------



## DrMueller (20. Juni 2007)

Noch ne letzte, ganz dumme Frage. Man kann bei FindWindow ja auch ClassName angeben. Nennt mich dumm, aber was wäre denn da gemeint?


----------



## ronaldh (20. Juni 2007)

Der Classname sollte die Art der Application sein. Um ein Window zu suchen, sollte er mit VbNullString (wie ich bereits oben im Beispiel genannt habe) initialisiert werden.

Ich habe das bisher noch nie anders gemacht, aber vermutlich kann hiermit nach bestimmten Arten von Applikationen gesucht werden. Bei einem VB6-Programm ist dies beispielsweise "ThunderRT6FormDC". Den Classname bekommst Du mit der API "GetClassName" heraus.

Viele Grüsse
ronaldh


----------



## DrMueller (20. Juni 2007)

Mal ne andere Frage: Ist es möglich mittels TaskID die hWnd, Namen etc rauszufinden?
Da bei uns die Forms dynamische Namen haben, kann ich mittels FindWindow leider net alle Fenster finden, die ich brauche.


----------



## ronaldh (20. Juni 2007)

Das müsste mit der API Funktion GetWindowThreadProcessId möglich sein.

Ein Beispiel findest Du im APIGuide.

Viele Grüsse
ronaldh


----------



## DrMueller (20. Juni 2007)

Mal was allgemeines, den Wert den man sucht bekommt man in der Funktion selber und net im Rückgabewert oder? Der Rückgabewert zeigt nur, ob alles korrekt abgelaufen ist?


----------

